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
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
[b3c185b6]97 ds_client_add_window(client, wnd);
[fd777a2]98 ds_display_add_window(client->display, wnd);
[6af4b4f]99
[a8eed5f]100 gfx_bitmap_params_init(&bparams);
[946a666]101 bparams.rect = params->rect;
102
[5d380b6]103 /* Allocate window bitmap */
104
[84e74ea]105 dgc = ds_display_get_gc(wnd->display);
[946a666]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
[0e6e77f]115 gfx_rect_dims(&params->rect, &dims);
[946a666]116 wnd->pixelmap.width = dims.x;
117 wnd->pixelmap.height = dims.y;
118 wnd->pixelmap.data = alloc.pixels;
[dbef30f]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);
[946a666]125 }
126
[1215db9]127 rc = mem_gc_create(&params->rect, &alloc, &ds_window_mem_gc_cb,
128 (void *)wnd, &wnd->mgc);
[dbef30f]129 if (rc != EOK)
130 goto error;
131
[3434233]132 wnd->rect = params->rect;
[9b502dd]133 wnd->min_size = params->min_size;
[dbef30f]134 wnd->gc = mem_gc_get_ctx(wnd->mgc);
[9242ad9]135 wnd->cursor = wnd->display->cursor[dcurs_arrow];
[9e84d2c]136 wnd->flags = params->flags;
137
[90f1f19]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 }
[9e84d2c]146
[5d380b6]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);
[9e84d2c]152
[5d380b6]153 /* Is this a popup window? */
[9e84d2c]154 if ((params->flags & wndf_popup) != 0)
155 ds_seat_set_popup(seat, wnd);
156 else
157 ds_seat_set_focus(seat, wnd);
158
[5d380b6]159 /* Is this window a panel? */
[29a5a99]160 if ((params->flags & wndf_avoid) != 0)
161 ds_display_update_max_rect(wnd->display);
162
[9e84d2c]163 (void) ds_display_paint(wnd->display, NULL);
164
[17c0f5d]165 *rwnd = wnd;
[c8cf261]166 return EOK;
167error:
[946a666]168 if (wnd != NULL) {
[5877de74]169 ds_client_remove_window(wnd);
170 ds_display_remove_window(wnd);
171 if (wnd->mgc != NULL)
172 mem_gc_delete(wnd->mgc);
[946a666]173 if (wnd->bitmap != NULL)
174 gfx_bitmap_destroy(wnd->bitmap);
[7cc30e9]175 if (wnd->caption != NULL)
176 free(wnd->caption);
[6af4b4f]177 free(wnd);
[946a666]178 }
179
[c8cf261]180 return rc;
181}
182
[0e6e77f]183/** Destroy window.
[c8cf261]184 *
[0e6e77f]185 * @param wnd Window
[c8cf261]186 */
[da412547]187void ds_window_destroy(ds_window_t *wnd)
[c8cf261]188{
[cc90846]189 ds_display_t *disp;
190
191 disp = wnd->display;
192
[ededdc4]193 ds_window_unfocus(wnd);
194
[b3c185b6]195 ds_client_remove_window(wnd);
[fd777a2]196 ds_display_remove_window(wnd);
[cc90846]197
[29a5a99]198 if ((wnd->flags & wndf_avoid) != 0)
199 ds_display_update_max_rect(disp);
200
[dbef30f]201 mem_gc_delete(wnd->mgc);
202
[946a666]203 if (wnd->bitmap != NULL)
204 gfx_bitmap_destroy(wnd->bitmap);
[c8cf261]205
[7cc30e9]206 free(wnd->caption);
[6af4b4f]207 free(wnd);
[cc90846]208
209 (void) ds_display_paint(disp, NULL);
[c8cf261]210}
211
[1a1271d]212/** Bring window to top.
213 *
214 * @param wnd Window
215 */
[b5c7cee]216void ds_window_bring_to_top(ds_window_t *wnd)
217{
[913add60]218 ds_display_window_to_top(wnd);
[2e0a2e7]219 (void) ds_display_paint(wnd->display, NULL);
[b5c7cee]220}
221
[6af4b4f]222/** Get generic graphic context from window.
[c8cf261]223 *
[6af4b4f]224 * @param wnd Window
[c8cf261]225 * @return Graphic context
226 */
[6af4b4f]227gfx_context_t *ds_window_get_ctx(ds_window_t *wnd)
[c8cf261]228{
[6af4b4f]229 return wnd->gc;
[c8cf261]230}
231
[06176e1]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
[2012fe0]242/** Paint a window using its backing bitmap.
[946a666]243 *
[2012fe0]244 * @param wnd Window to paint
245 * @param rect Display rectangle to paint to
[946a666]246 * @return EOK on success or an error code
247 */
[2012fe0]248errno_t ds_window_paint(ds_window_t *wnd, gfx_rect_t *rect)
[946a666]249{
[2012fe0]250 gfx_rect_t srect;
251 gfx_rect_t *brect;
252 gfx_rect_t crect;
253
[d19d15b]254 log_msg(LOG_DEFAULT, LVL_DEBUG2, "ds_window_paint");
[2012fe0]255
[06176e1]256 /* Skip painting the window if not visible */
257 if (!ds_window_is_visible(wnd))
258 return EOK;
259
[2012fe0]260 if (rect != NULL) {
261 gfx_rect_rtranslate(&wnd->dpos, rect, &srect);
262
263 /* Determine if we have anything to do */
[01c2759]264 gfx_rect_clip(&srect, &wnd->rect, &crect);
[2012fe0]265 if (gfx_rect_is_empty(&crect))
266 return EOK;
267
268 brect = &srect;
269 } else {
270 brect = NULL;
271 }
272
[f5191b4]273 /* This can happen in unit tests */
274 if (wnd->bitmap == NULL)
275 return EOK;
276
[2012fe0]277 return gfx_bitmap_render(wnd->bitmap, brect, &wnd->dpos);
[946a666]278}
279
[6301a24f]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.
[a40ae0d]284 *
285 * @param wnd Window
[6301a24f]286 * @param rect Place to store preview rectangle
[a40ae0d]287 */
[6301a24f]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)
[a40ae0d]317{
[6301a24f]318 errno_t rc;
[1b443cc0]319 gfx_color_t *color;
[6301a24f]320 gfx_rect_t prect;
[a65b0c8]321 gfx_rect_t dr;
322 gfx_rect_t pr;
[6301a24f]323 gfx_context_t *gc;
[1b443cc0]324
[6301a24f]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 }
[a40ae0d]334
[1b443cc0]335 rc = gfx_color_new_rgb_i16(0xffff, 0xffff, 0xffff, &color);
336 if (rc != EOK)
[6301a24f]337 return rc;
[1b443cc0]338
[84e74ea]339 gc = ds_display_get_gc(wnd->display);
[1b443cc0]340 if (gc != NULL) {
341 gfx_set_color(gc, color);
[a65b0c8]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
[1b443cc0]377 }
378
379 gfx_color_delete(color);
[6301a24f]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
[d19d15b]403 log_msg(LOG_DEFAULT, LVL_DEBUG2, "ds_window_repaint_preview");
[6301a24f]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
[3be5366]447 * @param pos_id Positioning device ID
[6301a24f]448 */
[3be5366]449static void ds_window_start_move(ds_window_t *wnd, gfx_coord2_t *pos,
450 sysarg_t pos_id)
[6301a24f]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;
[3be5366]459 wnd->orig_pos_id = pos_id;
[6301a24f]460 wnd->state = dsw_moving;
461 wnd->preview_pos = wnd->dpos;
462
463 (void) ds_window_repaint_preview(wnd, NULL);
[a40ae0d]464}
465
466/** Finish moving a window by mouse drag.
467 *
468 * @param wnd Window
[76a02db]469 * @param pos Position where mouse button was released
[a40ae0d]470 */
[76a02db]471static void ds_window_finish_move(ds_window_t *wnd, gfx_coord2_t *pos)
[a40ae0d]472{
473 gfx_coord2_t dmove;
474 gfx_coord2_t nwpos;
[6301a24f]475 gfx_rect_t old_rect;
[a40ae0d]476
477 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_finish_move (%d, %d)",
[76a02db]478 (int) pos->x, (int) pos->y);
[a40ae0d]479
[5f483be]480 assert(wnd->state == dsw_moving);
[a2e104e]481
[76a02db]482 gfx_coord2_subtract(pos, &wnd->orig_pos, &dmove);
[a40ae0d]483 gfx_coord2_add(&wnd->dpos, &dmove, &nwpos);
[6301a24f]484
485 ds_window_get_preview_rect(wnd, &old_rect);
486
[a40ae0d]487 wnd->dpos = nwpos;
488 wnd->state = dsw_idle;
[3be5366]489 wnd->orig_pos_id = 0;
[946a666]490
[2012fe0]491 (void) ds_display_paint(wnd->display, NULL);
[a40ae0d]492}
493
494/** Update window position when moving by mouse drag.
495 *
496 * @param wnd Window
[76a02db]497 * @param pos Current mouse position
[a40ae0d]498 */
[76a02db]499static void ds_window_update_move(ds_window_t *wnd, gfx_coord2_t *pos)
[a40ae0d]500{
501 gfx_coord2_t dmove;
502 gfx_coord2_t nwpos;
[6301a24f]503 gfx_rect_t old_rect;
[a40ae0d]504
[6e91475]505 log_msg(LOG_DEFAULT, LVL_DEBUG2, "ds_window_update_move (%d, %d)",
[76a02db]506 (int) pos->x, (int) pos->y);
[a40ae0d]507
[5f483be]508 assert(wnd->state == dsw_moving);
[a2e104e]509
[76a02db]510 gfx_coord2_subtract(pos, &wnd->orig_pos, &dmove);
[a40ae0d]511 gfx_coord2_add(&wnd->dpos, &dmove, &nwpos);
[c79545e]512
[6301a24f]513 ds_window_get_preview_rect(wnd, &old_rect);
514 wnd->preview_pos = nwpos;
[a40ae0d]515
[6301a24f]516 (void) ds_window_repaint_preview(wnd, &old_rect);
[a40ae0d]517}
518
[76a02db]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
[b0ae23f]524 * @param pos_id Positioning device ID
[76a02db]525 */
526static void ds_window_start_resize(ds_window_t *wnd,
[b0ae23f]527 display_wnd_rsztype_t rsztype, gfx_coord2_t *pos, sysarg_t pos_id)
[76a02db]528{
[9901f267]529 ds_seat_t *seat;
530 display_stock_cursor_t ctype;
531
[76a02db]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
[b0ae23f]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
[76a02db]543 wnd->orig_pos = *pos;
[b0ae23f]544 wnd->orig_pos_id = pos_id;
[76a02db]545 wnd->state = dsw_resizing;
546 wnd->rsztype = rsztype;
[71eff34]547 wnd->preview_rect = wnd->rect;
[9901f267]548
549 ctype = display_cursor_from_wrsz(rsztype);
550 ds_seat_set_wm_cursor(seat, wnd->display->cursor[ctype]);
[6301a24f]551
552 (void) ds_window_repaint_preview(wnd, NULL);
[76a02db]553}
554
[e022819]555/** Finish resizing a window by mouse drag.
556 *
557 * @param wnd Window
[76a02db]558 * @param pos Position where mouse button was released
[e022819]559 */
[76a02db]560static void ds_window_finish_resize(ds_window_t *wnd, gfx_coord2_t *pos)
[e022819]561{
562 gfx_coord2_t dresize;
563 gfx_rect_t nrect;
[9901f267]564 ds_seat_t *seat;
[e022819]565
566 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_finish_resize (%d, %d)",
[76a02db]567 (int) pos->x, (int) pos->y);
[e022819]568
[5f483be]569 assert(wnd->state == dsw_resizing);
[76a02db]570 gfx_coord2_subtract(pos, &wnd->orig_pos, &dresize);
[e022819]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);
[9901f267]577
[b0ae23f]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);
[6301a24f]582
[3be5366]583 wnd->orig_pos_id = 0;
584
[6301a24f]585 (void) ds_display_paint(wnd->display, NULL);
[e022819]586}
587
588/** Update window position when resizing by mouse drag.
589 *
590 * @param wnd Window
[76a02db]591 * @param pos Current mouse position
[e022819]592 */
[76a02db]593static void ds_window_update_resize(ds_window_t *wnd, gfx_coord2_t *pos)
[e022819]594{
595 gfx_coord2_t dresize;
596 gfx_rect_t nrect;
[6301a24f]597 gfx_rect_t old_rect;
[e022819]598
[d19d15b]599 log_msg(LOG_DEFAULT, LVL_DEBUG2, "ds_window_update_resize (%d, %d)",
[76a02db]600 (int) pos->x, (int) pos->y);
[e022819]601
[5f483be]602 assert(wnd->state == dsw_resizing);
[e022819]603
[76a02db]604 gfx_coord2_subtract(pos, &wnd->orig_pos, &dresize);
[e022819]605 ds_window_calc_resize(wnd, &dresize, &nrect);
606
[6301a24f]607 ds_window_get_preview_rect(wnd, &old_rect);
608 wnd->preview_rect = nrect;
609 (void) ds_window_repaint_preview(wnd, &old_rect);
[e022819]610}
611
[338d0935]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
[a40ae0d]634/** Post position event to window.
635 *
636 * @param wnd Window
637 * @param event Position event
[35cffea]638 *
639 * @return EOK on success or an error code
[a40ae0d]640 */
641errno_t ds_window_post_pos_event(ds_window_t *wnd, pos_event_t *event)
642{
[f7fb2b21]643 pos_event_t tevent;
[a2e104e]644 gfx_coord2_t pos;
[3be5366]645 sysarg_t pos_id;
[a2e104e]646 gfx_rect_t drect;
647 bool inside;
[f7fb2b21]648
[d19d15b]649 log_msg(LOG_DEFAULT, LVL_DEBUG2,
[78445be8]650 "ds_window_post_pos_event type=%d pos=%d,%d", event->type,
[a40ae0d]651 (int) event->hpos, (int) event->vpos);
652
[a2e104e]653 pos.x = event->hpos;
654 pos.y = event->vpos;
[3be5366]655 pos_id = event->pos_id;
[a2e104e]656 gfx_rect_translate(&wnd->dpos, &wnd->rect, &drect);
657 inside = gfx_pix_inside_rect(&pos, &drect);
[a40ae0d]658
[35cffea]659 if (event->type == POS_PRESS && event->btn_num == 2 && inside &&
660 (wnd->flags & wndf_maximized) == 0) {
[3be5366]661 ds_window_start_move(wnd, &pos, pos_id);
[5f483be]662 return EOK;
663 }
[a40ae0d]664
[e022819]665 if (event->type == POS_RELEASE) {
[6828a56]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)) {
[5f483be]669 ds_window_finish_move(wnd, &pos);
670 return EOK;
671 }
672
[6828a56]673 if (wnd->state == dsw_resizing &&
674 ds_window_orig_seat(wnd, pos_id)) {
[5f483be]675 ds_window_finish_resize(wnd, &pos);
676 return EOK;
677 }
[e022819]678 }
[a2e104e]679
[e022819]680 if (event->type == POS_UPDATE) {
[6828a56]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)) {
[5f483be]684 ds_window_update_move(wnd, &pos);
685 return EOK;
686 }
687
[6828a56]688 if (wnd->state == dsw_resizing &&
689 ds_window_orig_seat(wnd, pos_id)) {
[5f483be]690 ds_window_update_resize(wnd, &pos);
691 return EOK;
692 }
[e022819]693 }
[a40ae0d]694
[f7fb2b21]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);
[a40ae0d]701}
702
[b0a94854]703/** Post focus event to window.
704 *
705 * @param wnd Window
[35cffea]706 * @return EOK on success or an error code
[b0a94854]707 */
708errno_t ds_window_post_focus_event(ds_window_t *wnd)
709{
[46a47c0]710 display_wnd_focus_ev_t efocus;
[3c54869]711 errno_t rc;
712 ds_wmclient_t *wmclient;
713
[78445be8]714 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_post_focus_event");
[b0a94854]715
[3c54869]716 /* Increase focus counter */
717 ++wnd->nfocus;
[46a47c0]718 efocus.nfocus = wnd->nfocus;
719
720 rc = ds_client_post_focus_event(wnd->client, wnd, &efocus);
721 if (rc != EOK)
722 return rc;
[3c54869]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;
[b0a94854]732}
733
734/** Post unfocus event to window.
735 *
736 * @param wnd Window
[35cffea]737 * @return EOK on success or an error code
[b0a94854]738 */
739errno_t ds_window_post_unfocus_event(ds_window_t *wnd)
740{
[46a47c0]741 display_wnd_unfocus_ev_t eunfocus;
[3c54869]742 errno_t rc;
743 ds_wmclient_t *wmclient;
744
[78445be8]745 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_post_unfocus_event");
[b0a94854]746
[3c54869]747 /* Decrease focus counter */
748 --wnd->nfocus;
[46a47c0]749 eunfocus.nfocus = wnd->nfocus;
750
751 rc = ds_client_post_unfocus_event(wnd->client, wnd, &eunfocus);
752 if (rc != EOK)
753 return rc;
[3c54869]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;
[b0a94854]763}
764
[e022819]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
[3be5366]770 * @param pos_id Positioning device ID
[e022819]771 * @param event Button press event
772 */
[3be5366]773void ds_window_move_req(ds_window_t *wnd, gfx_coord2_t *pos, sysarg_t pos_id)
[e022819]774{
[76a02db]775 gfx_coord2_t orig_pos;
776
[e022819]777 log_msg(LOG_DEFAULT, LVL_DEBUG, "ds_window_move_req (%d, %d)",
778 (int) pos->x, (int) pos->y);
779
[76a02db]780 gfx_coord2_add(&wnd->dpos, pos, &orig_pos);
[3be5366]781 ds_window_start_move(wnd, &orig_pos, pos_id);
[e022819]782}
783
[0680854]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
[c9927c66]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
[35cffea]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{
[29a5a99]809 *rect = wnd->display->max_rect;
[35cffea]810}
811
[e022819]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
[b0ae23f]818 * @param pos_id Positioning device ID
[e022819]819 * @param event Button press event
820 */
821void ds_window_resize_req(ds_window_t *wnd, display_wnd_rsztype_t rsztype,
[b0ae23f]822 gfx_coord2_t *pos, sysarg_t pos_id)
[e022819]823{
[76a02db]824 gfx_coord2_t orig_pos;
825
[b0ae23f]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);
[e022819]828
[76a02db]829 gfx_coord2_add(&wnd->dpos, pos, &orig_pos);
[b0ae23f]830 ds_window_start_resize(wnd, rsztype, &orig_pos, pos_id);
[e022819]831}
832
[0680854]833/** Resize window.
834 *
835 * @param wnd Window
[35cffea]836 * @return EOK on success or an error code
[0680854]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
[84e74ea]850 dgc = ds_display_get_gc(wnd->display);
[0680854]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;
[dbef30f]877
878 /* Point memory GC to the new bitmap */
879 mem_gc_retarget(wnd->mgc, nrect, &alloc);
[0680854]880 }
881
882 gfx_coord2_add(&wnd->dpos, offs, &ndpos);
883
884 wnd->dpos = ndpos;
885 wnd->rect = *nrect;
886
[29a5a99]887 if ((wnd->flags & wndf_avoid) != 0)
888 ds_display_update_max_rect(wnd->display);
889
[0680854]890 (void) ds_display_paint(wnd->display, NULL);
891 return EOK;
892}
893
[06176e1]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
[17c0f5d]905 ds_window_unfocus(wnd);
906
[06176e1]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
[35cffea]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
[ad698f4]993 /* Clear maximized flag */
[35cffea]994 wnd->flags &= ~wndf_maximized;
995
996 return EOK;
997}
998
[e022819]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{
[9b502dd]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 }
[e022819]1036}
1037
[9242ad9]1038/** Set window cursor.
1039 *
1040 * @param wnd Window
[7cc30e9]1041 * @param cursor New cursor
[9242ad9]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
[7cc30e9]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;
[f1f433d]1065 ds_wmclient_t *wmclient;
[7cc30e9]1066
1067 dcaption = str_dup(caption);
1068 if (dcaption == NULL)
1069 return ENOMEM;
1070
1071 free(wnd->caption);
1072 wnd->caption = dcaption;
1073
[f1f433d]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
[7cc30e9]1081 return EOK;
1082}
1083
[17c0f5d]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 */
[acd7ac2]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,
[17c0f5d]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
[6828a56]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
[2ab8ab3]1209/** Window memory GC invalidate callback.
[dbef30f]1210 *
[2ab8ab3]1211 * This is called by the window's memory GC when a rectangle is modified.
[dbef30f]1212 */
[2ab8ab3]1213static void ds_window_invalidate_cb(void *arg, gfx_rect_t *rect)
[dbef30f]1214{
1215 ds_window_t *wnd = (ds_window_t *)arg;
1216 gfx_rect_t drect;
1217
1218 /* Repaint the corresponding part of the display */
[d74a2b8]1219
[dbef30f]1220 gfx_rect_translate(&wnd->dpos, rect, &drect);
[d74a2b8]1221 ds_display_lock(wnd->display);
[dbef30f]1222 (void) ds_display_paint(wnd->display, &drect);
[d74a2b8]1223 ds_display_unlock(wnd->display);
[dbef30f]1224}
1225
[2ab8ab3]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
[c8cf261]1237/** @}
1238 */
Note: See TracBrowser for help on using the repository browser.