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

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

Switch focus to the right window when window is closed

  • Property mode set to 100644
File size: 30.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_prev(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_next_window(wnd);
1101 while (nwnd != NULL && (nwnd->flags & ~allowed_flags) != 0) {
1102 nwnd = ds_display_next_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_first_window(wnd->display);
1112 while (nwnd != NULL && nwnd != wnd &&
1113 (nwnd->flags & ~allowed_flags) != 0) {
1114 nwnd = ds_display_next_window(nwnd);
1115 }
1116
1117 if (nwnd == wnd)
1118 return NULL;
1119
1120 return nwnd;
1121}
1122
1123/** Find alternate window with the allowed flags.
1124 *
1125 * An alternate window is a *different* window that is preferably previous
1126 * in the display order and only has the @a allowed flags.
1127 *
1128 * @param wnd Window
1129 * @param allowed_flags Bitmask of flags that the window is allowed to have
1130 *
1131 * @return Alternate window matching the criteria or @c NULL if there is none
1132 */
1133ds_window_t *ds_window_find_next(ds_window_t *wnd,
1134 display_wnd_flags_t allowed_flags)
1135{
1136 ds_window_t *nwnd;
1137
1138 /* Try preceding windows in display order */
1139 nwnd = ds_display_prev_window(wnd);
1140 while (nwnd != NULL && (nwnd->flags & ~allowed_flags) != 0) {
1141 nwnd = ds_display_prev_window(nwnd);
1142 }
1143
1144 /* Do we already have a matching window? */
1145 if (nwnd != NULL && (nwnd->flags & ~allowed_flags) == 0) {
1146 return nwnd;
1147 }
1148
1149 /* Try succeeding windows in display order */
1150 nwnd = ds_display_last_window(wnd->display);
1151 while (nwnd != NULL && nwnd != wnd &&
1152 (nwnd->flags & ~allowed_flags) != 0) {
1153 nwnd = ds_display_prev_window(nwnd);
1154 }
1155
1156 if (nwnd == wnd)
1157 return NULL;
1158
1159 return nwnd;
1160}
1161
1162/** Remove focus from window.
1163 *
1164 * Used to switch focus to another window when closing or minimizing window.
1165 *
1166 * @param wnd Window
1167 */
1168void ds_window_unfocus(ds_window_t *wnd)
1169{
1170 ds_seat_t *seat;
1171
1172 /* Make sure window is no longer focused in any seat */
1173 seat = ds_display_first_seat(wnd->display);
1174 while (seat != NULL) {
1175 ds_seat_unfocus_wnd(seat, wnd);
1176 seat = ds_display_next_seat(seat);
1177 }
1178}
1179
1180/** Determine if input device belongs to the same seat as the original device.
1181 *
1182 * Compare the seat ownning @a idev_id with the seat owning @a wnd->orig_pos_id
1183 * (the device that started the window move or resize).
1184 *
1185 * This is used to make sure that, when two seats focus the same window,
1186 * only devices owned by the seat that started the resize or move can
1187 * affect it. Otherwise moving the other pointer(s) would disrupt the
1188 * resize or move operation.
1189 *
1190 * @param wnd Window (that is currently being resized or moved)
1191 * @param idev_id Input device ID
1192 * @return @c true iff idev_id is owned by the same seat as the input
1193 * device that started the resize or move
1194 */
1195bool ds_window_orig_seat(ds_window_t *wnd, sysarg_t idev_id)
1196{
1197 ds_seat_t *orig_seat;
1198 ds_seat_t *seat;
1199
1200 /* Window must be in state such that wnd->orig_pos_id is valid */
1201 assert(wnd->state == dsw_moving || wnd->state == dsw_resizing);
1202
1203 orig_seat = ds_display_seat_by_idev(wnd->display, wnd->orig_pos_id);
1204 seat = ds_display_seat_by_idev(wnd->display, idev_id);
1205
1206 return seat == orig_seat;
1207}
1208
1209/** Window memory GC invalidate callback.
1210 *
1211 * This is called by the window's memory GC when a rectangle is modified.
1212 */
1213static void ds_window_invalidate_cb(void *arg, gfx_rect_t *rect)
1214{
1215 ds_window_t *wnd = (ds_window_t *)arg;
1216 gfx_rect_t drect;
1217
1218 /* Repaint the corresponding part of the display */
1219
1220 gfx_rect_translate(&wnd->dpos, rect, &drect);
1221 ds_display_lock(wnd->display);
1222 (void) ds_display_paint(wnd->display, &drect);
1223 ds_display_unlock(wnd->display);
1224}
1225
1226/** Window memory GC update callback.
1227 *
1228 * This is called by the window's memory GC when it is to be updated.
1229 */
1230static void ds_window_update_cb(void *arg)
1231{
1232 ds_window_t *wnd = (ds_window_t *)arg;
1233
1234 (void) wnd;
1235}
1236
1237/** @}
1238 */
Note: See TracBrowser for help on using the repository browser.