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

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

Make sure menus are not obscured by task bar

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