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

Last change on this file was 0d00e53, checked in by Jiri Svoboda <jiri@…>, 8 months ago

Shut down dialog

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