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

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

Create menu windows in the correct seat

Add a mechanism to set the seat of a new display window, UI window,
UI popup - input device ID. This is set to the ID of the device which
activated the menu (mouse, keyboard). The display server determines
the correct seat from there.

This makes sure clicking outside closes the correct pop-up window.

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