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

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

Add pos_id information to move request, too

This will become useful momentarily.

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