source: mainline/uspace/srv/hid/display/window.c@ b0ae23f

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since b0ae23f was b0ae23f, checked in by Jiri Svoboda <jiri@…>, 3 years ago

Change the correct pointer's shape when resizing window

The request to resize a window originates from the client. The display
server forces the cursor to a double-arrow shape regardless of whether
it is over the window or not, until the resize is done. This is to
make sure the cursor keeps that shape even if it moves outside of
the current boundaries of the window. With multiple pointers we need
to know which one to change. This is done by passing the pos_id from
button press event that starts the resize all the way to
ds_window_start_resize(). Then it needs to be stored in the window
structure for use when it is time to restore the cursor.

  • Property mode set to 100644
File size: 27.2 KB
Line 
1/*
2 * Copyright (c) 2023 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup display
30 * @{
31 */
32/**
33 * @file Display server window
34 */
35
36#include <gfx/bitmap.h>
37#include <gfx/color.h>
38#include <gfx/coord.h>
39#include <gfx/context.h>
40#include <gfx/render.h>
41#include <io/log.h>
42#include <io/pixelmap.h>
43#include <macros.h>
44#include <memgfx/memgc.h>
45#include <stdlib.h>
46#include <str.h>
47#include <wndmgt.h>
48#include "client.h"
49#include "display.h"
50#include "seat.h"
51#include "window.h"
52#include "wmclient.h"
53
54static void ds_window_invalidate_cb(void *, gfx_rect_t *);
55static void ds_window_update_cb(void *);
56static void ds_window_get_preview_rect(ds_window_t *, gfx_rect_t *);
57
58static mem_gc_cb_t ds_window_mem_gc_cb = {
59 .invalidate = ds_window_invalidate_cb,
60 .update = ds_window_update_cb
61};
62
63/** Create window.
64 *
65 * Create graphics context for rendering into a window.
66 *
67 * @param client Client owning the window
68 * @param params Window parameters
69 * @param rwnd Place to store pointer to new window.
70 *
71 * @return EOK on success or an error code
72 */
73errno_t ds_window_create(ds_client_t *client, display_wnd_params_t *params,
74 ds_window_t **rwnd)
75{
76 ds_window_t *wnd = NULL;
77 ds_seat_t *seat;
78 gfx_context_t *dgc;
79 gfx_coord2_t dims;
80 gfx_bitmap_params_t bparams;
81 gfx_bitmap_alloc_t alloc;
82 errno_t rc;
83
84 wnd = calloc(1, sizeof(ds_window_t));
85 if (wnd == NULL) {
86 rc = ENOMEM;
87 goto error;
88 }
89
90 wnd->caption = str_dup(params->caption);
91 if (wnd->caption == NULL) {
92 rc = ENOMEM;
93 goto error;
94 }
95
96 ds_client_add_window(client, wnd);
97 ds_display_add_window(client->display, wnd);
98
99 gfx_bitmap_params_init(&bparams);
100 bparams.rect = params->rect;
101
102 dgc = ds_display_get_gc(wnd->display);
103 if (dgc != NULL) {
104 rc = gfx_bitmap_create(dgc, &bparams, NULL, &wnd->bitmap);
105 if (rc != EOK)
106 goto error;
107
108 rc = gfx_bitmap_get_alloc(wnd->bitmap, &alloc);
109 if (rc != EOK)
110 goto error;
111
112 gfx_rect_dims(&params->rect, &dims);
113 wnd->pixelmap.width = dims.x;
114 wnd->pixelmap.height = dims.y;
115 wnd->pixelmap.data = alloc.pixels;
116 } else {
117 /* This is just for unit tests */
118 gfx_rect_dims(&params->rect, &dims);
119 alloc.pitch = dims.x * sizeof(uint32_t);
120 alloc.off0 = 0;
121 alloc.pixels = calloc(1, alloc.pitch * dims.y);
122 }
123
124 rc = mem_gc_create(&params->rect, &alloc, &ds_window_mem_gc_cb,
125 (void *)wnd, &wnd->mgc);
126 if (rc != EOK)
127 goto error;
128
129 wnd->rect = params->rect;
130 wnd->min_size = params->min_size;
131 wnd->gc = mem_gc_get_ctx(wnd->mgc);
132 wnd->cursor = wnd->display->cursor[dcurs_arrow];
133 wnd->flags = params->flags;
134
135 if ((params->flags & wndf_setpos) != 0) {
136 /* Specific window position */
137 wnd->dpos = params->pos;
138 } else {
139 /* Automatic window placement */
140 wnd->dpos.x = ((wnd->id - 1) & 1) * 400;
141 wnd->dpos.y = ((wnd->id - 1) & 2) / 2 * 300;
142 }
143
144 // TODO Multi-seat: which seat should own the new window?
145 seat = ds_display_first_seat(client->display);
146
147 if ((params->flags & wndf_popup) != 0)
148 ds_seat_set_popup(seat, wnd);
149 else
150 ds_seat_set_focus(seat, wnd);
151
152 if ((params->flags & wndf_avoid) != 0)
153 ds_display_update_max_rect(wnd->display);
154
155 (void) ds_display_paint(wnd->display, NULL);
156
157 *rwnd = wnd;
158 return EOK;
159error:
160 if (wnd != NULL) {
161 ds_client_remove_window(wnd);
162 ds_display_remove_window(wnd);
163 if (wnd->mgc != NULL)
164 mem_gc_delete(wnd->mgc);
165 if (wnd->bitmap != NULL)
166 gfx_bitmap_destroy(wnd->bitmap);
167 if (wnd->caption != NULL)
168 free(wnd->caption);
169 free(wnd);
170 }
171
172 return rc;
173}
174
175/** Destroy window.
176 *
177 * @param wnd Window
178 */
179void ds_window_destroy(ds_window_t *wnd)
180{
181 ds_display_t *disp;
182
183 disp = wnd->display;
184
185 ds_window_unfocus(wnd);
186
187 ds_client_remove_window(wnd);
188 ds_display_remove_window(wnd);
189
190 if ((wnd->flags & wndf_avoid) != 0)
191 ds_display_update_max_rect(disp);
192
193 mem_gc_delete(wnd->mgc);
194
195 if (wnd->bitmap != NULL)
196 gfx_bitmap_destroy(wnd->bitmap);
197
198 free(wnd->caption);
199 free(wnd);
200
201 (void) ds_display_paint(disp, NULL);
202}
203
204/** Bring window to top.
205 *
206 * @param wnd Window
207 */
208void ds_window_bring_to_top(ds_window_t *wnd)
209{
210 ds_display_window_to_top(wnd);
211 (void) ds_display_paint(wnd->display, NULL);
212}
213
214/** Get generic graphic context from window.
215 *
216 * @param wnd Window
217 * @return Graphic context
218 */
219gfx_context_t *ds_window_get_ctx(ds_window_t *wnd)
220{
221 return wnd->gc;
222}
223
224/** Determine if window is visible.
225 *
226 * @param wnd Window
227 * @return @c true iff window is visible
228 */
229bool ds_window_is_visible(ds_window_t *wnd)
230{
231 return (wnd->flags & wndf_minimized) == 0;
232}
233
234/** Paint a window using its backing bitmap.
235 *
236 * @param wnd Window to paint
237 * @param rect Display rectangle to paint to
238 * @return EOK on success or an error code
239 */
240errno_t ds_window_paint(ds_window_t *wnd, gfx_rect_t *rect)
241{
242 gfx_rect_t srect;
243 gfx_rect_t *brect;
244 gfx_rect_t crect;
245
246 log_msg(LOG_DEFAULT, LVL_DEBUG2, "ds_window_paint");
247
248 /* Skip painting the window if not visible */
249 if (!ds_window_is_visible(wnd))
250 return EOK;
251
252 if (rect != NULL) {
253 gfx_rect_rtranslate(&wnd->dpos, rect, &srect);
254
255 /* Determine if we have anything to do */
256 gfx_rect_clip(&srect, &wnd->rect, &crect);
257 if (gfx_rect_is_empty(&crect))
258 return EOK;
259
260 brect = &srect;
261 } else {
262 brect = NULL;
263 }
264
265 /* This can happen in unit tests */
266 if (wnd->bitmap == NULL)
267 return EOK;
268
269 return gfx_bitmap_render(wnd->bitmap, brect, &wnd->dpos);
270}
271
272/** Get the preview rectangle for a window.
273 *
274 * Get the preview rectangle if the window is being resized or moved.
275 * If the window is not being resized or moved, return an empty rectangle.
276 *
277 * @param wnd Window
278 * @param rect Place to store preview rectangle
279 */
280static void ds_window_get_preview_rect(ds_window_t *wnd, gfx_rect_t *rect)
281{
282 switch (wnd->state) {
283 case dsw_idle:
284 break;
285 case dsw_moving:
286 gfx_rect_translate(&wnd->preview_pos, &wnd->rect, rect);
287 return;
288 case dsw_resizing:
289 gfx_rect_translate(&wnd->dpos, &wnd->preview_rect, rect);
290 return;
291 }
292
293 rect->p0.x = 0;
294 rect->p0.y = 0;
295 rect->p1.x = 0;
296 rect->p1.y = 0;
297}
298
299/** Paint window preview if the window is being moved or resized.
300 *
301 * If the window is not being resized or moved, take no action and return
302 * success.
303 *
304 * @param wnd Window for which to paint preview
305 * @param rect Clipping rectangle
306 * @return EOK on success or an error code
307 */
308errno_t ds_window_paint_preview(ds_window_t *wnd, gfx_rect_t *rect)
309{
310 errno_t rc;
311 gfx_color_t *color;
312 gfx_rect_t prect;
313 gfx_rect_t dr;
314 gfx_rect_t pr;
315 gfx_context_t *gc;
316
317 /*
318 * Get preview rectangle. If the window is not being resized/moved,
319 * we should get an empty rectangle.
320 */
321 ds_window_get_preview_rect(wnd, &prect);
322 if (gfx_rect_is_empty(&prect)) {
323 /* There is nothing to paint */
324 return EOK;
325 }
326
327 rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
328 if (rc != EOK)
329 return rc;
330
331 gc = ds_display_get_gc(wnd->display);
332 if (gc != NULL) {
333 gfx_set_color(gc, color);
334
335 /*
336 * TODO: Ideally we'd want XOR operation to make the preview
337 * frame visible on any background. If we wanted to get really
338 * fancy, we'd fill it with a pattern
339 */
340
341 pr.p0.x = prect.p0.x;
342 pr.p0.y = prect.p0.y;
343 pr.p1.x = prect.p1.x;
344 pr.p1.y = prect.p0.y + 1;
345 gfx_rect_clip(&pr, rect, &dr);
346 gfx_fill_rect(gc, &dr);
347
348 pr.p0.x = prect.p0.x;
349 pr.p0.y = prect.p1.y - 1;
350 pr.p1.x = prect.p1.x;
351 pr.p1.y = prect.p1.y;
352 gfx_rect_clip(&pr, rect, &dr);
353 gfx_fill_rect(gc, &dr);
354
355 pr.p0.x = prect.p0.x;
356 pr.p0.y = prect.p0.y;
357 pr.p1.x = prect.p0.x + 1;
358 pr.p1.y = prect.p1.y;
359 gfx_rect_clip(&pr, rect, &dr);
360 gfx_fill_rect(gc, &dr);
361
362 pr.p0.x = prect.p1.x - 1;
363 pr.p0.y = prect.p0.y;
364 pr.p1.x = prect.p1.x;
365 pr.p1.y = prect.p1.y;
366 gfx_rect_clip(&pr, rect, &dr);
367 gfx_fill_rect(gc, &dr);
368
369 }
370
371 gfx_color_delete(color);
372 return EOK;
373}
374
375/** Repaint window preview when resizing or moving.
376 *
377 * Repaint the window preview wich was previously at rectangle @a old_rect.
378 * The current preview rectangle is determined from window state. If
379 * the window did not previously have a preview, @a old_rect should point
380 * to an empty rectangle or be NULL. When window has finished
381 * moving or resizing, the preview will be cleared.
382 *
383 * @param wnd Window for which to paint preview
384 * @param rect Clipping rectangle
385 * @return EOK on success or an error code
386 */
387static errno_t ds_window_repaint_preview(ds_window_t *wnd, gfx_rect_t *old_rect)
388{
389 errno_t rc;
390 gfx_rect_t prect;
391 gfx_rect_t envelope;
392 bool oldr;
393 bool newr;
394
395 log_msg(LOG_DEFAULT, LVL_DEBUG2, "ds_window_repaint_preview");
396
397 /*
398 * Get current preview rectangle. If the window is not being resized/moved,
399 * we should get an empty rectangle.
400 */
401 ds_window_get_preview_rect(wnd, &prect);
402
403 oldr = (old_rect != NULL) && !gfx_rect_is_empty(old_rect);
404 newr = !gfx_rect_is_empty(&prect);
405
406 if (oldr && newr && gfx_rect_is_incident(old_rect, &prect)) {
407 /*
408 * As an optimization, repaint both rectangles in a single
409 * operation.
410 */
411
412 gfx_rect_envelope(old_rect, &prect, &envelope);
413
414 rc = ds_display_paint(wnd->display, &envelope);
415 if (rc != EOK)
416 return rc;
417 } else {
418 /* Repaint each rectangle separately */
419 if (oldr) {
420 rc = ds_display_paint(wnd->display, old_rect);
421 if (rc != EOK)
422 return rc;
423 }
424
425 if (newr) {
426 rc = ds_display_paint(wnd->display, &prect);
427 if (rc != EOK)
428 return rc;
429 }
430 }
431
432 return EOK;
433}
434
435/** Start moving a window by mouse drag.
436 *
437 * @param wnd Window
438 * @param pos Position where mouse button was pressed
439 */
440static void ds_window_start_move(ds_window_t *wnd, gfx_coord2_t *pos)
441{
442 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_start_move (%d, %d)",
443 (int) pos->x, (int) pos->y);
444
445 if (wnd->state != dsw_idle)
446 return;
447
448 wnd->orig_pos = *pos;
449 wnd->state = dsw_moving;
450 wnd->preview_pos = wnd->dpos;
451
452 (void) ds_window_repaint_preview(wnd, NULL);
453}
454
455/** Finish moving a window by mouse drag.
456 *
457 * @param wnd Window
458 * @param pos Position where mouse button was released
459 */
460static void ds_window_finish_move(ds_window_t *wnd, gfx_coord2_t *pos)
461{
462 gfx_coord2_t dmove;
463 gfx_coord2_t nwpos;
464 gfx_rect_t old_rect;
465
466 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_finish_move (%d, %d)",
467 (int) pos->x, (int) pos->y);
468
469 assert(wnd->state == dsw_moving);
470
471 gfx_coord2_subtract(pos, &wnd->orig_pos, &dmove);
472 gfx_coord2_add(&wnd->dpos, &dmove, &nwpos);
473
474 ds_window_get_preview_rect(wnd, &old_rect);
475
476 wnd->dpos = nwpos;
477 wnd->state = dsw_idle;
478
479 (void) ds_display_paint(wnd->display, NULL);
480}
481
482/** Update window position when moving by mouse drag.
483 *
484 * @param wnd Window
485 * @param pos Current mouse position
486 */
487static void ds_window_update_move(ds_window_t *wnd, gfx_coord2_t *pos)
488{
489 gfx_coord2_t dmove;
490 gfx_coord2_t nwpos;
491 gfx_rect_t old_rect;
492
493 log_msg(LOG_DEFAULT, LVL_DEBUG2, "ds_window_update_move (%d, %d)",
494 (int) pos->x, (int) pos->y);
495
496 assert(wnd->state == dsw_moving);
497
498 gfx_coord2_subtract(pos, &wnd->orig_pos, &dmove);
499 gfx_coord2_add(&wnd->dpos, &dmove, &nwpos);
500
501 ds_window_get_preview_rect(wnd, &old_rect);
502 wnd->preview_pos = nwpos;
503
504 (void) ds_window_repaint_preview(wnd, &old_rect);
505}
506
507/** Start resizing a window by mouse drag.
508 *
509 * @param wnd Window
510 * @param rsztype Resize type (which part of window is being dragged)
511 * @param pos Position where mouse button was pressed
512 * @param pos_id Positioning device ID
513 */
514static void ds_window_start_resize(ds_window_t *wnd,
515 display_wnd_rsztype_t rsztype, gfx_coord2_t *pos, sysarg_t pos_id)
516{
517 ds_seat_t *seat;
518 display_stock_cursor_t ctype;
519
520 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_start_resize (%d, %d)",
521 (int) pos->x, (int) pos->y);
522
523 if (wnd->state != dsw_idle)
524 return;
525
526 /* Determine which seat started the resize */
527 seat = ds_display_seat_by_idev(wnd->display, pos_id);
528 if (seat == NULL)
529 return;
530
531 wnd->orig_pos = *pos;
532 wnd->orig_pos_id = pos_id;
533 wnd->state = dsw_resizing;
534 wnd->rsztype = rsztype;
535 wnd->preview_rect = wnd->rect;
536
537 ctype = display_cursor_from_wrsz(rsztype);
538 ds_seat_set_wm_cursor(seat, wnd->display->cursor[ctype]);
539
540 (void) ds_window_repaint_preview(wnd, NULL);
541}
542
543/** Finish resizing a window by mouse drag.
544 *
545 * @param wnd Window
546 * @param pos Position where mouse button was released
547 */
548static void ds_window_finish_resize(ds_window_t *wnd, gfx_coord2_t *pos)
549{
550 gfx_coord2_t dresize;
551 gfx_rect_t nrect;
552 ds_seat_t *seat;
553
554 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_finish_resize (%d, %d)",
555 (int) pos->x, (int) pos->y);
556
557 assert(wnd->state == dsw_resizing);
558 gfx_coord2_subtract(pos, &wnd->orig_pos, &dresize);
559
560 /* Compute new rectangle */
561 ds_window_calc_resize(wnd, &dresize, &nrect);
562
563 wnd->state = dsw_idle;
564 ds_client_post_resize_event(wnd->client, wnd, &nrect);
565
566 /* Determine which seat started the resize */
567 seat = ds_display_seat_by_idev(wnd->display, wnd->orig_pos_id);
568 if (seat != NULL)
569 ds_seat_set_wm_cursor(seat, NULL);
570
571 (void) ds_display_paint(wnd->display, NULL);
572}
573
574/** Update window position when resizing by mouse drag.
575 *
576 * @param wnd Window
577 * @param pos Current mouse position
578 */
579static void ds_window_update_resize(ds_window_t *wnd, gfx_coord2_t *pos)
580{
581 gfx_coord2_t dresize;
582 gfx_rect_t nrect;
583 gfx_rect_t old_rect;
584
585 log_msg(LOG_DEFAULT, LVL_DEBUG2, "ds_window_update_resize (%d, %d)",
586 (int) pos->x, (int) pos->y);
587
588 assert(wnd->state == dsw_resizing);
589
590 gfx_coord2_subtract(pos, &wnd->orig_pos, &dresize);
591 ds_window_calc_resize(wnd, &dresize, &nrect);
592
593 ds_window_get_preview_rect(wnd, &old_rect);
594 wnd->preview_rect = nrect;
595 (void) ds_window_repaint_preview(wnd, &old_rect);
596}
597
598/** Post keyboard event to window.
599 *
600 * @param wnd Window
601 * @param event Event
602 *
603 * @return EOK on success or an error code
604 */
605errno_t ds_window_post_kbd_event(ds_window_t *wnd, kbd_event_t *event)
606{
607 bool alt_or_shift;
608
609 alt_or_shift = event->mods & (KM_SHIFT | KM_ALT);
610
611 if (event->type == KEY_PRESS && alt_or_shift && event->key == KC_F4) {
612 /* On Alt-F4 or Shift-F4 send close event to the window */
613 ds_client_post_close_event(wnd->client, wnd);
614 return EOK;
615 }
616
617 return ds_client_post_kbd_event(wnd->client, wnd, event);
618}
619
620/** Post position event to window.
621 *
622 * @param wnd Window
623 * @param event Position event
624 *
625 * @return EOK on success or an error code
626 */
627errno_t ds_window_post_pos_event(ds_window_t *wnd, pos_event_t *event)
628{
629 pos_event_t tevent;
630 gfx_coord2_t pos;
631 gfx_rect_t drect;
632 bool inside;
633
634 log_msg(LOG_DEFAULT, LVL_DEBUG2,
635 "ds_window_post_pos_event type=%d pos=%d,%d", event->type,
636 (int) event->hpos, (int) event->vpos);
637
638 pos.x = event->hpos;
639 pos.y = event->vpos;
640 gfx_rect_translate(&wnd->dpos, &wnd->rect, &drect);
641 inside = gfx_pix_inside_rect(&pos, &drect);
642
643 if (event->type == POS_PRESS && event->btn_num == 2 && inside &&
644 (wnd->flags & wndf_maximized) == 0) {
645 ds_window_start_move(wnd, &pos);
646 return EOK;
647 }
648
649 if (event->type == POS_RELEASE) {
650 if (wnd->state == dsw_moving) {
651 ds_window_finish_move(wnd, &pos);
652 return EOK;
653 }
654
655 if (wnd->state == dsw_resizing) {
656 ds_window_finish_resize(wnd, &pos);
657 return EOK;
658 }
659 }
660
661 if (event->type == POS_UPDATE) {
662 if (wnd->state == dsw_moving) {
663 ds_window_update_move(wnd, &pos);
664 return EOK;
665 }
666
667 if (wnd->state == dsw_resizing) {
668 ds_window_update_resize(wnd, &pos);
669 return EOK;
670 }
671 }
672
673 /* Transform event coordinates to window-local */
674 tevent = *event;
675 tevent.hpos -= wnd->dpos.x;
676 tevent.vpos -= wnd->dpos.y;
677
678 return ds_client_post_pos_event(wnd->client, wnd, &tevent);
679}
680
681/** Post focus event to window.
682 *
683 * @param wnd Window
684 * @return EOK on success or an error code
685 */
686errno_t ds_window_post_focus_event(ds_window_t *wnd)
687{
688 display_wnd_focus_ev_t efocus;
689 errno_t rc;
690 ds_wmclient_t *wmclient;
691
692 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_post_focus_event");
693
694 /* Increase focus counter */
695 ++wnd->nfocus;
696 efocus.nfocus = wnd->nfocus;
697
698 rc = ds_client_post_focus_event(wnd->client, wnd, &efocus);
699 if (rc != EOK)
700 return rc;
701
702 /* Notify window managers about window information change */
703 wmclient = ds_display_first_wmclient(wnd->display);
704 while (wmclient != NULL) {
705 ds_wmclient_post_wnd_changed_event(wmclient, wnd->id);
706 wmclient = ds_display_next_wmclient(wmclient);
707 }
708
709 return EOK;
710}
711
712/** Post unfocus event to window.
713 *
714 * @param wnd Window
715 * @return EOK on success or an error code
716 */
717errno_t ds_window_post_unfocus_event(ds_window_t *wnd)
718{
719 display_wnd_unfocus_ev_t eunfocus;
720 errno_t rc;
721 ds_wmclient_t *wmclient;
722
723 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_post_unfocus_event");
724
725 /* Decrease focus counter */
726 --wnd->nfocus;
727 eunfocus.nfocus = wnd->nfocus;
728
729 rc = ds_client_post_unfocus_event(wnd->client, wnd, &eunfocus);
730 if (rc != EOK)
731 return rc;
732
733 /* Notify window managers about window information change */
734 wmclient = ds_display_first_wmclient(wnd->display);
735 while (wmclient != NULL) {
736 ds_wmclient_post_wnd_changed_event(wmclient, wnd->id);
737 wmclient = ds_display_next_wmclient(wmclient);
738 }
739
740 return EOK;
741}
742
743/** Start moving a window, detected by client.
744 *
745 * @param wnd Window
746 * @param pos Position where the pointer was when the move started
747 * relative to the window
748 * @param event Button press event
749 */
750void ds_window_move_req(ds_window_t *wnd, gfx_coord2_t *pos)
751{
752 gfx_coord2_t orig_pos;
753
754 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_move_req (%d, %d)",
755 (int) pos->x, (int) pos->y);
756
757 gfx_coord2_add(&wnd->dpos, pos, &orig_pos);
758 ds_window_start_move(wnd, &orig_pos);
759}
760
761/** Move window.
762 *
763 * @param wnd Window
764 */
765void ds_window_move(ds_window_t *wnd, gfx_coord2_t *dpos)
766{
767 wnd->dpos = *dpos;
768 (void) ds_display_paint(wnd->display, NULL);
769}
770
771/** Get window position.
772 *
773 * @param wnd Window
774 */
775void ds_window_get_pos(ds_window_t *wnd, gfx_coord2_t *dpos)
776{
777 *dpos = wnd->dpos;
778}
779
780/** Get maximized window rectangle.
781 *
782 * @param wnd Window
783 */
784void ds_window_get_max_rect(ds_window_t *wnd, gfx_rect_t *rect)
785{
786 *rect = wnd->display->max_rect;
787}
788
789/** Start resizing a window, detected by client.
790 *
791 * @param wnd Window
792 * @param rsztype Resize type (which part of window is being dragged)
793 * @param pos Position where the pointer was when the resize started
794 * relative to the window
795 * @param pos_id Positioning device ID
796 * @param event Button press event
797 */
798void ds_window_resize_req(ds_window_t *wnd, display_wnd_rsztype_t rsztype,
799 gfx_coord2_t *pos, sysarg_t pos_id)
800{
801 gfx_coord2_t orig_pos;
802
803 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_resize_req (%d, %d, %d, %d)",
804 (int)rsztype, (int)pos->x, (int)pos->y, (int)pos_id);
805
806 gfx_coord2_add(&wnd->dpos, pos, &orig_pos);
807 ds_window_start_resize(wnd, rsztype, &orig_pos, pos_id);
808}
809
810/** Resize window.
811 *
812 * @param wnd Window
813 * @return EOK on success or an error code
814 */
815errno_t ds_window_resize(ds_window_t *wnd, gfx_coord2_t *offs,
816 gfx_rect_t *nrect)
817{
818 gfx_context_t *dgc;
819 gfx_bitmap_params_t bparams;
820 gfx_bitmap_t *nbitmap;
821 pixelmap_t npixelmap;
822 gfx_coord2_t dims;
823 gfx_bitmap_alloc_t alloc;
824 gfx_coord2_t ndpos;
825 errno_t rc;
826
827 dgc = ds_display_get_gc(wnd->display);
828 if (dgc != NULL) {
829 gfx_bitmap_params_init(&bparams);
830 bparams.rect = *nrect;
831
832 rc = gfx_bitmap_create(dgc, &bparams, NULL, &nbitmap);
833 if (rc != EOK)
834 return ENOMEM;
835
836 rc = gfx_bitmap_get_alloc(nbitmap, &alloc);
837 if (rc != EOK) {
838 gfx_bitmap_destroy(nbitmap);
839 return ENOMEM;
840 }
841
842 gfx_rect_dims(nrect, &dims);
843 npixelmap.width = dims.x;
844 npixelmap.height = dims.y;
845 npixelmap.data = alloc.pixels;
846
847 /* TODO: Transfer contents within overlap */
848
849 if (wnd->bitmap != NULL)
850 gfx_bitmap_destroy(wnd->bitmap);
851
852 wnd->bitmap = nbitmap;
853 wnd->pixelmap = npixelmap;
854
855 /* Point memory GC to the new bitmap */
856 mem_gc_retarget(wnd->mgc, nrect, &alloc);
857 }
858
859 gfx_coord2_add(&wnd->dpos, offs, &ndpos);
860
861 wnd->dpos = ndpos;
862 wnd->rect = *nrect;
863
864 if ((wnd->flags & wndf_avoid) != 0)
865 ds_display_update_max_rect(wnd->display);
866
867 (void) ds_display_paint(wnd->display, NULL);
868 return EOK;
869}
870
871/** Minimize window.
872 *
873 * @param wnd Window
874 * @return EOK on success or an error code
875 */
876errno_t ds_window_minimize(ds_window_t *wnd)
877{
878 /* If already minimized, do nothing and return success. */
879 if ((wnd->flags & wndf_minimized) != 0)
880 return EOK;
881
882 ds_window_unfocus(wnd);
883
884 wnd->flags |= wndf_minimized;
885 (void) ds_display_paint(wnd->display, NULL);
886 return EOK;
887}
888
889/** Unminimize window.
890 *
891 * @param wnd Window
892 * @return EOK on success or an error code
893 */
894errno_t ds_window_unminimize(ds_window_t *wnd)
895{
896 /* If not minimized, do nothing and return success. */
897 if ((wnd->flags & wndf_minimized) == 0)
898 return EOK;
899
900 wnd->flags &= ~wndf_minimized;
901 (void) ds_display_paint(wnd->display, NULL);
902 return EOK;
903}
904
905/** Maximize window.
906 *
907 * @param wnd Window
908 * @return EOK on success or an error code
909 */
910errno_t ds_window_maximize(ds_window_t *wnd)
911{
912 gfx_coord2_t old_dpos;
913 gfx_rect_t old_rect;
914 gfx_coord2_t offs;
915 gfx_rect_t max_rect;
916 gfx_rect_t nrect;
917 errno_t rc;
918
919 /* If already maximized, do nothing and return success. */
920 if ((wnd->flags & wndf_maximized) != 0)
921 return EOK;
922
923 /* Remember the old window rectangle and display position */
924 old_rect = wnd->rect;
925 old_dpos = wnd->dpos;
926
927 ds_window_get_max_rect(wnd, &max_rect);
928
929 /* Keep window contents on the same position on the screen */
930 offs.x = max_rect.p0.x - wnd->dpos.x;
931 offs.y = max_rect.p0.y - wnd->dpos.y;
932
933 /* Maximized window's coordinates will start at 0,0 */
934 gfx_rect_rtranslate(&max_rect.p0, &max_rect, &nrect);
935
936 rc = ds_window_resize(wnd, &offs, &nrect);
937 if (rc != EOK)
938 return rc;
939
940 /* Set window flags, remember normal rectangle */
941 wnd->flags |= wndf_maximized;
942 wnd->normal_rect = old_rect;
943 wnd->normal_dpos = old_dpos;
944
945 return EOK;
946}
947
948/** Unmaximize window.
949 *
950 * @param wnd Window
951 * @return EOK on success or an error code
952 */
953errno_t ds_window_unmaximize(ds_window_t *wnd)
954{
955 gfx_coord2_t offs;
956 errno_t rc;
957
958 /* If not maximized, do nothing and return success. */
959 if ((wnd->flags & wndf_maximized) == 0)
960 return EOK;
961
962 /* Keep window contents on the same position on the screen */
963 offs.x = wnd->normal_dpos.x - wnd->dpos.x;
964 offs.y = wnd->normal_dpos.y - wnd->dpos.y;
965
966 rc = ds_window_resize(wnd, &offs, &wnd->normal_rect);
967 if (rc != EOK)
968 return rc;
969
970 /* Clear maximized flag */
971 wnd->flags &= ~wndf_maximized;
972
973 return EOK;
974}
975
976/** Compute new window rectangle after resize operation.
977 *
978 * @param wnd Window which is being resized (in dsw_resizing state and thus
979 * has rsztype set)
980 * @param dresize Amount by which to resize
981 * @param nrect Place to store new rectangle
982 */
983void ds_window_calc_resize(ds_window_t *wnd, gfx_coord2_t *dresize,
984 gfx_rect_t *nrect)
985{
986 if ((wnd->rsztype & display_wr_top) != 0) {
987 nrect->p0.y = min(wnd->rect.p0.y + dresize->y,
988 wnd->rect.p1.y - wnd->min_size.y);
989 } else {
990 nrect->p0.y = wnd->rect.p0.y;
991 }
992
993 if ((wnd->rsztype & display_wr_left) != 0) {
994 nrect->p0.x = min(wnd->rect.p0.x + dresize->x,
995 wnd->rect.p1.x - wnd->min_size.x);
996 } else {
997 nrect->p0.x = wnd->rect.p0.x;
998 }
999
1000 if ((wnd->rsztype & display_wr_bottom) != 0) {
1001 nrect->p1.y = max(wnd->rect.p1.y + dresize->y,
1002 wnd->rect.p0.y + wnd->min_size.y);
1003 } else {
1004 nrect->p1.y = wnd->rect.p1.y;
1005 }
1006
1007 if ((wnd->rsztype & display_wr_right) != 0) {
1008 nrect->p1.x = max(wnd->rect.p1.x + dresize->x,
1009 wnd->rect.p0.x + wnd->min_size.x);
1010 } else {
1011 nrect->p1.x = wnd->rect.p1.x;
1012 }
1013}
1014
1015/** Set window cursor.
1016 *
1017 * @param wnd Window
1018 * @param cursor New cursor
1019 * @return EOK on success, EINVAL if @a cursor is invalid
1020 */
1021errno_t ds_window_set_cursor(ds_window_t *wnd, display_stock_cursor_t cursor)
1022{
1023 if (cursor >= dcurs_arrow &&
1024 cursor < (display_stock_cursor_t) dcurs_limit) {
1025 wnd->cursor = wnd->display->cursor[cursor];
1026 return EOK;
1027 } else {
1028 return EINVAL;
1029 }
1030}
1031
1032/** Set window caption.
1033 *
1034 * @param wnd Window
1035 * @param caption New caption
1036 *
1037 * @return EOK on success, EINVAL if @a cursor is invalid
1038 */
1039errno_t ds_window_set_caption(ds_window_t *wnd, const char *caption)
1040{
1041 char *dcaption;
1042 ds_wmclient_t *wmclient;
1043
1044 dcaption = str_dup(caption);
1045 if (dcaption == NULL)
1046 return ENOMEM;
1047
1048 free(wnd->caption);
1049 wnd->caption = dcaption;
1050
1051 /* Notify window managers about window information change */
1052 wmclient = ds_display_first_wmclient(wnd->display);
1053 while (wmclient != NULL) {
1054 ds_wmclient_post_wnd_changed_event(wmclient, wnd->id);
1055 wmclient = ds_display_next_wmclient(wmclient);
1056 }
1057
1058 return EOK;
1059}
1060
1061/** Find alternate window with the allowed flags.
1062 *
1063 * An alternate window is a *different* window that is preferably previous
1064 * in the display order and only has the @a allowed flags.
1065 *
1066 * @param wnd Window
1067 * @param allowed_flags Bitmask of flags that the window is allowed to have
1068 *
1069 * @return Alternate window matching the criteria or @c NULL if there is none
1070 */
1071ds_window_t *ds_window_find_alt(ds_window_t *wnd,
1072 display_wnd_flags_t allowed_flags)
1073{
1074 ds_window_t *nwnd;
1075
1076 /* Try preceding windows in display order */
1077 nwnd = ds_display_prev_window(wnd);
1078 while (nwnd != NULL && (nwnd->flags & ~allowed_flags) != 0) {
1079 nwnd = ds_display_prev_window(nwnd);
1080 }
1081
1082 /* Do we already have a matching window? */
1083 if (nwnd != NULL && (nwnd->flags & ~allowed_flags) == 0) {
1084 return nwnd;
1085 }
1086
1087 /* Try succeeding windows in display order */
1088 nwnd = ds_display_last_window(wnd->display);
1089 while (nwnd != NULL && nwnd != wnd &&
1090 (nwnd->flags & ~allowed_flags) != 0) {
1091 nwnd = ds_display_prev_window(nwnd);
1092 }
1093
1094 if (nwnd == wnd)
1095 return NULL;
1096
1097 return nwnd;
1098}
1099
1100/** Remove focus from window.
1101 *
1102 * Used to switch focus to another window when closing or minimizing window.
1103 *
1104 * @param wnd Window
1105 */
1106void ds_window_unfocus(ds_window_t *wnd)
1107{
1108 ds_seat_t *seat;
1109
1110 /* Make sure window is no longer focused in any seat */
1111 seat = ds_display_first_seat(wnd->display);
1112 while (seat != NULL) {
1113 ds_seat_unfocus_wnd(seat, wnd);
1114 seat = ds_display_next_seat(seat);
1115 }
1116}
1117
1118/** Window memory GC invalidate callback.
1119 *
1120 * This is called by the window's memory GC when a rectangle is modified.
1121 */
1122static void ds_window_invalidate_cb(void *arg, gfx_rect_t *rect)
1123{
1124 ds_window_t *wnd = (ds_window_t *)arg;
1125 gfx_rect_t drect;
1126
1127 /* Repaint the corresponding part of the display */
1128
1129 gfx_rect_translate(&wnd->dpos, rect, &drect);
1130 ds_display_lock(wnd->display);
1131 (void) ds_display_paint(wnd->display, &drect);
1132 ds_display_unlock(wnd->display);
1133}
1134
1135/** Window memory GC update callback.
1136 *
1137 * This is called by the window's memory GC when it is to be updated.
1138 */
1139static void ds_window_update_cb(void *arg)
1140{
1141 ds_window_t *wnd = (ds_window_t *)arg;
1142
1143 (void) wnd;
1144}
1145
1146/** @}
1147 */
Note: See TracBrowser for help on using the repository browser.