From 7fe60435bce6595a9c58a9bfd8244d74b5320e96 Mon Sep 17 00:00:00 2001 From: Benjamin Franzke Date: Tue, 15 Jan 2013 08:46:13 +0100 Subject: Import DirectFB141_2k11R3_beta5 --- Source/DirectFB/wm/default/default.c | 4073 ++++++++++++++++++++++++++++++++++ 1 file changed, 4073 insertions(+) create mode 100755 Source/DirectFB/wm/default/default.c (limited to 'Source/DirectFB/wm/default/default.c') diff --git a/Source/DirectFB/wm/default/default.c b/Source/DirectFB/wm/default/default.c new file mode 100755 index 0000000..2049c2d --- /dev/null +++ b/Source/DirectFB/wm/default/default.c @@ -0,0 +1,4073 @@ +/* + (c) Copyright 2001-2009 The world wide DirectFB Open Source Community (directfb.org) + (c) Copyright 2000-2004 Convergence (integrated media) GmbH + + All rights reserved. + + Written by Denis Oliver Kropp , + Andreas Hundt , + Sven Neumann , + Ville Syrjälä and + Claudio Ciccani . + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + + +D_DEBUG_DOMAIN( WM_Default, "WM/Default", "Default window manager module" ); + + +DFB_WINDOW_MANAGER( default ) + + +typedef struct { + DirectLink link; + + DFBInputDeviceKeySymbol symbol; + DFBInputDeviceModifierMask modifiers; + + CoreWindow *owner; +} GrabbedKey; + +/**************************************************************************************************/ + +#define MAX_KEYS 16 +#define MAX_UPDATE_REGIONS 8 + +typedef struct { + CoreDFB *core; +} WMData; + +typedef struct { + int magic; + + CoreWindowStack *stack; + + DFBUpdates updates; + DFBRegion update_regions[MAX_UPDATE_REGIONS]; + + DFBInputDeviceButtonMask buttons; + DFBInputDeviceModifierMask modifiers; + DFBInputDeviceLockState locks; + + bool active; + + int wm_level; + int wm_cycle; + + FusionVector windows; + + CoreWindow *pointer_window; /* window grabbing the pointer */ + CoreWindow *keyboard_window; /* window grabbing the keyboard */ + CoreWindow *focused_window; /* window having the focus */ + CoreWindow *entered_window; /* window under the pointer */ + CoreWindow *unselkeys_window; /* window grabbing unselected keys */ + + DirectLink *grabbed_keys; /* List of currently grabbed keys. */ + + struct { + DFBInputDeviceKeySymbol symbol; + DFBInputDeviceKeyIdentifier id; + int code; + CoreWindow *owner; + } keys[MAX_KEYS]; + + CoreSurface *cursor_bs; /* backing store for region under cursor */ + bool cursor_bs_valid; + DFBRegion cursor_region; + bool cursor_drawn; + + int cursor_dx; + int cursor_dy; +} StackData; + +typedef struct { + int magic; + + CoreWindow *window; + + StackData *stack_data; + + int priority; /* derived from stacking class */ + + CoreLayerRegionConfig config; +} WindowData; + +/**************************************************************************************************/ + +static DFBResult +restack_window( CoreWindow *window, + WindowData *window_data, + CoreWindow *relative, + WindowData *relative_data, + int relation, + DFBWindowStackingClass stacking ); + +static DFBResult +update_window( CoreWindow *window, + WindowData *window_data, + const DFBRegion *region, + DFBSurfaceFlipFlags flags, + bool force_complete, + bool force_invisible, + bool scale_region ); + +/**************************************************************************************************/ + +static int keys_compare( const void *key1, + const void *key2 ) +{ + return *(const DFBInputDeviceKeySymbol*) key1 - *(const DFBInputDeviceKeySymbol*) key2; +} + +/**************************************************************************************************/ + +static inline void +transform_point_in_window( CoreWindow *window, + int *x, + int *y ) +{ + int _x = *x, _y = *y; + + switch (window->config.rotation) { + default: + D_BUG( "invalid rotation %d", window->config.rotation ); + case 0: + break; + + case 90: + *x = window->config.bounds.w - _y - 1; + *y = _x; + break; + + case 180: + *x = window->config.bounds.w - _x - 1; + *y = window->config.bounds.h - _y - 1; + break; + + case 270: + *x = _y; + *y = window->config.bounds.h - _x - 1; + break; + } +} + +static void +post_event( CoreWindow *window, + StackData *data, + DFBWindowEvent *event ) +{ + D_ASSERT( window != NULL ); + D_ASSERT( window->stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( event != NULL ); + + event->buttons = data->buttons; + event->modifiers = data->modifiers; + event->locks = data->locks; + + dfb_window_post_event( window, event ); +} + +static void +send_key_event( CoreWindow *window, + StackData *data, + const DFBInputEvent *event ) +{ + DFBWindowEvent we; + + D_ASSERT( window != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( event != NULL ); + + we.type = (event->type == DIET_KEYPRESS) ? DWET_KEYDOWN : DWET_KEYUP; + we.key_code = event->key_code; + we.key_id = event->key_id; + we.key_symbol = event->key_symbol; + + post_event( window, data, &we ); +} + +static void +send_button_event( CoreWindow *window, + StackData *data, + const DFBInputEvent *event ) +{ + DFBWindowEvent we; + + D_ASSERT( window != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( event != NULL ); + + we.type = (event->type == DIET_BUTTONPRESS) ? DWET_BUTTONDOWN : DWET_BUTTONUP; + we.x = window->stack->cursor.x - window->config.bounds.x; + we.y = window->stack->cursor.y - window->config.bounds.y; + we.button = (data->wm_level & 2) ? (event->button + 2) : event->button; + + transform_point_in_window( window, &we.x, &we.y ); + + post_event( window, data, &we ); +} + +/**************************************************************************************************/ + +static inline void +transform_window_to_stack( CoreWindow *window, + const DFBRectangle *rect, + DFBRectangle *ret_rect ) +{ + DFB_RECTANGLE_ASSERT( rect ); + + ret_rect->x = rect->x; + ret_rect->y = rect->y; + + switch (window->config.rotation) { + default: + D_BUG( "invalid rotation %d", window->config.rotation ); + case 0: + case 180: + ret_rect->w = rect->w; + ret_rect->h = rect->h; + break; + + case 90: + case 270: + ret_rect->w = rect->h; + ret_rect->h = rect->w; + break; + } +} + +static inline int +get_priority( const CoreWindow *window ) +{ + D_ASSERT( window != NULL ); + + switch (window->config.stacking) { + case DWSC_UPPER: + return 1; + + case DWSC_MIDDLE: + return 0; + + case DWSC_LOWER: + return -1; + + default: + D_BUG( "unknown stacking class" ); + break; + } + + return 0; +} + +static inline int +get_index( const StackData *data, + const CoreWindow *window ) +{ + D_ASSERT( data != NULL ); + D_ASSERT( window != NULL ); + + D_ASSERT( fusion_vector_contains( &data->windows, window ) ); + + return fusion_vector_index_of( &data->windows, window ); +} + +static CoreWindow * +get_keyboard_window( CoreWindowStack *stack, + StackData *data, + const DFBInputEvent *event ) +{ + DirectLink *l; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( event != NULL ); + D_ASSERT( event->type == DIET_KEYPRESS || event->type == DIET_KEYRELEASE ); + + /* Check explicit key grabs first. */ + direct_list_foreach (l, data->grabbed_keys) { + GrabbedKey *key = (GrabbedKey*) l; + + if (key->symbol == event->key_symbol && + key->modifiers == data->modifiers) + return key->owner; + } + + /* Don't do implicit grabs on keys without a hardware index. */ + if (event->key_code == -1) + return (data->keyboard_window ? + data->keyboard_window : data->focused_window); + + /* Implicitly grab (press) or ungrab (release) key. */ + if (event->type == DIET_KEYPRESS) { + int i; + int free_key = -1; + CoreWindow *window; + + /* Check active grabs. */ + for (i=0; ikeys[i].code == event->key_code) + return data->keys[i].owner; + + /* Remember first free array item. */ + if (free_key == -1 && data->keys[i].code == -1) + free_key = i; + } + + /* Key is not grabbed, check for explicit keyboard grab or focus. */ + window = data->keyboard_window ? + data->keyboard_window : data->focused_window; + if (!window) + return NULL; + + /* Check key selection. */ + switch (window->config.key_selection) { + case DWKS_ALL: + break; + + case DWKS_LIST: + D_ASSERT( window->config.keys != NULL ); + D_ASSERT( window->config.num_keys > 0 ); + + if (bsearch( &event->key_symbol, + window->config.keys, window->config.num_keys, + sizeof(DFBInputDeviceKeySymbol), keys_compare )) + break; + + /* fall through */ + + case DWKS_NONE: + return data->unselkeys_window; + } + + /* Check if a free array item was found. */ + if (free_key == -1) { + D_WARN( "maximum number of owned keys reached" ); + return NULL; + } + + /* Implicitly grab the key. */ + data->keys[free_key].symbol = event->key_symbol; + data->keys[free_key].id = event->key_id; + data->keys[free_key].code = event->key_code; + data->keys[free_key].owner = window; + + return window; + } + else { + int i; + + /* Lookup owner and ungrab the key. */ + for (i=0; ikeys[i].code == event->key_code) { + data->keys[i].code = -1; + + /* Return owner (NULL if destroyed). */ + return data->keys[i].owner; + } + } + } + + /* No owner for release event found, discard it. */ + return NULL; +} + +static CoreWindow* +window_at_pointer( CoreWindowStack *stack, + StackData *data, + WMData *wmdata, + int x, + int y ) +{ + int i; + CoreWindow *window; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + + if (!stack->cursor.enabled) { + fusion_vector_foreach_reverse (window, i, data->windows) + if (window->config.opacity && !(window->config.options & DWOP_GHOST)) + return window; + + return NULL; + } + + if (x < 0) + x = stack->cursor.x; + if (y < 0) + y = stack->cursor.y; + + fusion_vector_foreach_reverse (window, i, data->windows) { + CoreWindowConfig *config = &window->config; + DFBWindowOptions options = config->options; + DFBRectangle rotated; + DFBRectangle *bounds = &rotated; + + transform_window_to_stack( window, &config->bounds, &rotated ); + + if (!(options & DWOP_GHOST) && config->opacity && + x >= bounds->x && x < bounds->x + bounds->w && + y >= bounds->y && y < bounds->y + bounds->h) + { + int wx = x - bounds->x; + int wy = y - bounds->y; + + if ( !(options & DWOP_SHAPED) || + !(options &(DWOP_ALPHACHANNEL|DWOP_COLORKEYING)) + || !window->surface || + ((options & DWOP_OPAQUE_REGION) && + (wx >= config->opaque.x1 && wx <= config->opaque.x2 && + wy >= config->opaque.y1 && wy <= config->opaque.y2))) + { + return window; + } + else { + u8 buf[8]; + CoreSurface *surface = window->surface; + DFBSurfacePixelFormat format = surface->config.format; + DFBRectangle rect = { wx, wy, 1, 1 }; + + if (dfb_surface_read_buffer( surface, CSBR_FRONT, buf, 8, &rect ) == DFB_OK) { + if (options & DWOP_ALPHACHANNEL) { + int alpha = -1; + + D_ASSERT( DFB_PIXELFORMAT_HAS_ALPHA( format ) ); + + switch (format) { + case DSPF_AiRGB: + alpha = 0xff - (*(u32*)(buf) >> 24); + break; + case DSPF_ARGB: + case DSPF_AYUV: + alpha = *(u32*)(buf) >> 24; + break; + case DSPF_ARGB1555: + case DSPF_ARGB2554: + case DSPF_ARGB4444: + alpha = *(u16*)(buf) & 0x8000; + alpha = alpha ? 0xff : 0x00; + break; + case DSPF_RGBA4444: + alpha = *(u16*)(buf) & 0x0008; + alpha = alpha ? 0xff : 0x00; + break; + case DSPF_ALUT44: + alpha = *(u8*)(buf) & 0xf0; + alpha |= alpha >> 4; + break; + case DSPF_LUT2: + case DSPF_LUT8: { + CorePalette *palette = surface->palette; + u8 pix = *((u8*) buf); + + if (palette && pix < palette->num_entries) { + alpha = palette->entries[pix].a; + break; + } + + + /* fall through */ + } + + default: + D_ONCE( "unknown format 0x%x", surface->config.format ); + break; + } + + if (alpha) /* alpha == -1 on error */ + return window; + } + if (options & DWOP_COLORKEYING) { + int pixel = 0; + u8 *p; + switch (format) { + case DSPF_ARGB: + case DSPF_AiRGB: + case DSPF_RGB32: + pixel = *(u32*)(buf) & 0x00ffffff; + break; + + case DSPF_RGB24: + p = (buf); +#ifdef WORDS_BIGENDIAN + pixel = (p[0] << 16) | (p[1] << 8) | p[2]; +#else + pixel = (p[2] << 16) | (p[1] << 8) | p[0]; +#endif + break; + + case DSPF_RGB16: + pixel = *(u16*)(buf); + break; + + case DSPF_ARGB4444: + case DSPF_RGB444: + pixel = *(u16*)(buf) + & 0x0fff; + break; + + case DSPF_RGBA4444: + pixel = *(u16*)(buf) + & 0xfff0; + break; + + case DSPF_ARGB1555: + case DSPF_RGB555: + case DSPF_BGR555: + pixel = *(u16*)(buf) + & 0x7fff; + break; + + case DSPF_RGB332: + case DSPF_LUT8: + pixel = *(u8*)(buf); + break; + + case DSPF_ALUT44: + pixel = *(u8*)(buf) + & 0x0f; + break; + + default: + D_ONCE( "unknown format 0x%x", surface->config.format ); + break; + } + + if (pixel != config->color_key) + return window; + } + } + } + } + } + + return NULL; +} + +static void +switch_focus( CoreWindowStack *stack, + StackData *data, + CoreWindow *to ) +{ + DFBWindowEvent evt; + CoreWindow *from; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + + from = data->focused_window; + + if (from == to) + return; + + if (to && to->caps & DWCAPS_NOFOCUS) + return; + + if (from) { + evt.type = DWET_LOSTFOCUS; + + post_event( from, data, &evt ); + } + + if (to) { + if (to->surface && to->surface->palette && !stack->hw_mode) { + CoreSurface *surface; + + D_ASSERT( to->primary_region != NULL ); + + if (dfb_layer_region_get_surface( to->primary_region, &surface ) == DFB_OK) { + if (DFB_PIXELFORMAT_IS_INDEXED( surface->config.format )) + dfb_surface_set_palette( surface, to->surface->palette ); + + dfb_surface_unref( surface ); + } + } + + evt.type = DWET_GOTFOCUS; + + post_event( to, data, &evt ); + } + + data->focused_window = to; +} + +static bool +update_focus( CoreWindowStack *stack, + StackData *data, + WMData *wmdata ) +{ + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + + /* if pointer is not grabbed */ + if (!data->pointer_window) { + CoreWindow *before = data->entered_window; + CoreWindow *after = window_at_pointer( stack, data, wmdata, -1, -1 ); + + /* and the window under the cursor is another one now */ + if (before != after) { + DFBWindowEvent we; + + /* send leave event */ + if (before) { + we.type = DWET_LEAVE; + we.x = stack->cursor.x - before->config.bounds.x; + we.y = stack->cursor.y - before->config.bounds.y; + + transform_point_in_window( before, &we.x, &we.y ); + + post_event( before, data, &we ); + } + + /* switch focus and send enter event */ + switch_focus( stack, data, after ); + + if (after) { + we.type = DWET_ENTER; + we.x = stack->cursor.x - after->config.bounds.x; + we.y = stack->cursor.y - after->config.bounds.y; + + transform_point_in_window( after, &we.x, &we.y ); + + post_event( after, data, &we ); + } + + /* update pointer to window under the cursor */ + data->entered_window = after; + + return true; + } + } + + return false; +} + +static void +ensure_focus( CoreWindowStack *stack, + StackData *data ) +{ + int i; + CoreWindow *window; + + if (data->focused_window) + return; + + fusion_vector_foreach_reverse (window, i, data->windows) { + if (window->config.opacity && !(window->config.options & DWOP_GHOST)) { + switch_focus( stack, data, window ); + break; + } + } +} + +/**************************************************************************************************/ +/**************************************************************************************************/ + +static inline void +transform_stack_to_dest( CoreWindowStack *stack, + const DFBRegion *region, + DFBRegion *ret_dest ) +{ + DFBDimension size = { stack->width, stack->height }; + + DFB_REGION_ASSERT( region ); + + dfb_region_from_rotated( ret_dest, region, &size, stack->rotation ); +} + +static void +draw_cursor( CoreWindowStack *stack, StackData *data, CardState *state, const DFBRegion *region ) +{ + DFBRectangle src; + DFBRegion dest; + DFBSurfaceBlittingFlags flags = DSBLIT_BLEND_ALPHACHANNEL; + + D_ASSERT( stack != NULL ); + D_MAGIC_ASSERT( data, StackData ); + D_MAGIC_ASSERT( state, CardState ); + DFB_REGION_ASSERT( region ); + + D_ASSUME( stack->cursor.opacity > 0 ); + + /* Initialize destination region. */ + transform_stack_to_dest( stack, region, &dest ); + + /* Initialize source rectangle. */ + src.x = region->x1 - stack->cursor.x + stack->cursor.hot.x; + src.y = region->y1 - stack->cursor.y + stack->cursor.hot.y; + src.w = region->x2 - region->x1 + 1; + src.h = region->y2 - region->y1 + 1; + + /* Use global alpha blending. */ + if (stack->cursor.opacity != 0xFF) { + flags |= DSBLIT_BLEND_COLORALPHA; + + /* Set opacity as blending factor. */ + if (state->color.a != stack->cursor.opacity) { + state->color.a = stack->cursor.opacity; + state->modified |= SMF_COLOR; + } + } + + /* Different compositing methods depending on destination format. */ + if (flags & DSBLIT_BLEND_ALPHACHANNEL) { + if (DFB_PIXELFORMAT_HAS_ALPHA( state->destination->config.format )) { + /* + * Always use compliant Porter/Duff SRC_OVER, + * if the destination has an alpha channel. + * + * Cd = destination color (non-premultiplied) + * Ad = destination alpha + * + * Cs = source color (non-premultiplied) + * As = source alpha + * + * Ac = color alpha + * + * cd = Cd * Ad (premultiply destination) + * cs = Cs * As (premultiply source) + * + * The full equation to calculate resulting color and alpha (premultiplied): + * + * cx = cd * (1-As*Ac) + cs * Ac + * ax = Ad * (1-As*Ac) + As * Ac + */ + dfb_state_set_src_blend( state, DSBF_ONE ); + + /* Need to premultiply source with As*Ac or only with Ac? */ + if (! (stack->cursor.surface->config.caps & DSCAPS_PREMULTIPLIED)) + flags |= DSBLIT_SRC_PREMULTIPLY; + else if (flags & DSBLIT_BLEND_COLORALPHA) + flags |= DSBLIT_SRC_PREMULTCOLOR; + + /* Need to premultiply/demultiply destination? */ +// if (! (state->destination->caps & DSCAPS_PREMULTIPLIED)) +// flags |= DSBLIT_DST_PREMULTIPLY | DSBLIT_DEMULTIPLY; + } + else { + /* + * We can avoid DSBLIT_SRC_PREMULTIPLY for destinations without an alpha channel + * by using another blending function, which is more likely that it's accelerated + * than premultiplication at this point in time. + * + * This way the resulting alpha (ax) doesn't comply with SRC_OVER, + * but as the destination doesn't have an alpha channel it's no problem. + * + * As the destination's alpha value is always 1.0 there's no need for + * premultiplication. The resulting alpha value will also be 1.0 without + * exceptions, therefore no need for demultiplication. + * + * cx = Cd * (1-As*Ac) + Cs*As * Ac (still same effect as above) + * ax = Ad * (1-As*Ac) + As*As * Ac (wrong, but discarded anyways) + */ + if (stack->cursor.surface->config.caps & DSCAPS_PREMULTIPLIED) { + /* Need to premultiply source with Ac? */ + if (flags & DSBLIT_BLEND_COLORALPHA) + flags |= DSBLIT_SRC_PREMULTCOLOR; + + dfb_state_set_src_blend( state, DSBF_ONE ); + } + else + dfb_state_set_src_blend( state, DSBF_SRCALPHA ); + } + } + + /* Set blitting flags. */ + dfb_state_set_blitting_flags( state, flags | stack->rotated_blit ); + + /* Set blitting source. */ + state->source = stack->cursor.surface; + state->modified |= SMF_SOURCE; + + /* Blit from the window to the region being updated. */ + dfb_gfxcard_blit( &src, dest.x1, dest.y1, state ); + + /* Reset blitting source. */ + state->source = NULL; + state->modified |= SMF_SOURCE; +} + +static void +draw_window( CoreWindow *window, CardState *state, + const DFBRegion *region, bool alpha_channel ) +{ + DFBRegion dest; + DFBSurfaceBlittingFlags flags = DSBLIT_NOFX; + CoreWindowStack *stack; + CoreWindowConfig *config; + CoreSurface *surface; + int rotation; + + D_ASSERT( window != NULL ); + D_MAGIC_ASSERT( state, CardState ); + DFB_REGION_ASSERT( region ); + + stack = window->stack; + D_MAGIC_ASSERT( stack, CoreWindowStack ); + + surface = window->surface; + D_MAGIC_ASSERT( surface, CoreSurface ); + + config = &window->config; + + /* Initialize destination region. */ + transform_stack_to_dest( stack, region, &dest ); + + /* Use per pixel alpha blending. */ + if (alpha_channel && (config->options & DWOP_ALPHACHANNEL)) + flags |= DSBLIT_BLEND_ALPHACHANNEL; + + /* Use global alpha blending. */ + if (config->opacity != 0xFF) { + flags |= DSBLIT_BLEND_COLORALPHA; + + /* Set opacity as blending factor. */ + if (state->color.a != config->opacity) { + state->color.a = config->opacity; + state->modified |= SMF_COLOR; + } + } + + /* Use source color keying. */ + if (config->options & DWOP_COLORKEYING) { + flags |= DSBLIT_SRC_COLORKEY; + + /* Set window color key. */ + dfb_state_set_src_colorkey( state, config->color_key ); + } + + /* Use automatic deinterlacing. */ + if (surface->config.caps & DSCAPS_INTERLACED) + flags |= DSBLIT_DEINTERLACE; + + /* Different compositing methods depending on destination format. */ + if (flags & (DSBLIT_BLEND_ALPHACHANNEL | DSBLIT_BLEND_COLORALPHA)) { + if (DFB_PIXELFORMAT_HAS_ALPHA( state->destination->config.format )) { + /* + * Always use compliant Porter/Duff SRC_OVER, + * if the destination has an alpha channel. + * + * Cd = destination color (non-premultiplied) + * Ad = destination alpha + * + * Cs = source color (non-premultiplied) + * As = source alpha + * + * Ac = color alpha + * + * cd = Cd * Ad (premultiply destination) + * cs = Cs * As (premultiply source) + * + * The full equation to calculate resulting color and alpha (premultiplied): + * + * cx = cd * (1-As*Ac) + cs * Ac + * ax = Ad * (1-As*Ac) + As * Ac + */ + dfb_state_set_src_blend( state, DSBF_ONE ); + + /* Need to premultiply source with As*Ac or only with Ac? */ + if (! (surface->config.caps & DSCAPS_PREMULTIPLIED)) + flags |= DSBLIT_SRC_PREMULTIPLY; + else if (flags & DSBLIT_BLEND_COLORALPHA) + flags |= DSBLIT_SRC_PREMULTCOLOR; + + /* Need to premultiply/demultiply destination? */ +// if (! (state->destination->caps & DSCAPS_PREMULTIPLIED)) +// flags |= DSBLIT_DST_PREMULTIPLY | DSBLIT_DEMULTIPLY; + } + else { + /* + * We can avoid DSBLIT_SRC_PREMULTIPLY for destinations without an alpha channel + * by using another blending function, which is more likely that it's accelerated + * than premultiplication at this point in time. + * + * This way the resulting alpha (ax) doesn't comply with SRC_OVER, + * but as the destination doesn't have an alpha channel it's no problem. + * + * As the destination's alpha value is always 1.0 there's no need for + * premultiplication. The resulting alpha value will also be 1.0 without + * exceptions, therefore no need for demultiplication. + * + * cx = Cd * (1-As*Ac) + Cs*As * Ac (still same effect as above) + * ax = Ad * (1-As*Ac) + As*As * Ac (wrong, but discarded anyways) + */ + if (surface->config.caps & DSCAPS_PREMULTIPLIED) { + /* Need to premultiply source with Ac? */ + if (flags & DSBLIT_BLEND_COLORALPHA) + flags |= DSBLIT_SRC_PREMULTCOLOR; + + dfb_state_set_src_blend( state, DSBF_ONE ); + } + else + dfb_state_set_src_blend( state, DSBF_SRCALPHA ); + } + } + + rotation = (window->config.rotation + stack->rotation) % 360; + switch (rotation) { + default: + D_BUG( "invalid rotation %d", rotation ); + case 0: + break; + + case 90: + flags |= DSBLIT_ROTATE90; + break; + + case 180: + flags |= DSBLIT_ROTATE180; + break; + + case 270: + flags |= DSBLIT_ROTATE270; + break; + } + + /* Set blitting flags. */ + dfb_state_set_blitting_flags( state, flags ); + + /* Set blitting source. */ + state->source = surface; + state->modified |= SMF_SOURCE; + + if (window->config.options & DWOP_SCALE) { + DFBDimension size = { stack->width, stack->height }; + DFBRegion clip = state->clip; + DFBRectangle src = { 0, 0, surface->config.size.w, surface->config.size.h }; + DFBRectangle dst; + DFBRectangle bounds; + + transform_window_to_stack( window, &window->config.bounds, &bounds ); + + dfb_rectangle_from_rotated( &dst, &bounds, &size, stack->rotation ); + + /* Change clipping region. */ + dfb_state_set_clip( state, &dest ); + + /* Scale window to the screen clipped by the region being updated. */ + dfb_gfxcard_stretchblit( &src, &dst, state ); + + /* Restore clipping region. */ + dfb_state_set_clip( state, &clip ); + } + else { + DFBDimension size = { config->bounds.w, config->bounds.h }; + DFBRectangle rect, src; + + D_ASSERT( surface->config.size.w == config->bounds.w ); + D_ASSERT( surface->config.size.h == config->bounds.h ); + + /* Initialize source rectangle. */ + dfb_rectangle_from_region( &rect, region ); + + /* Subtract window offset. */ + rect.x -= config->bounds.x; + rect.y -= config->bounds.y; + + /* Rotate back to window surface. */ + if (window->config.rotation == 90 || window->config.rotation == 270) + D_UTIL_SWAP( size.w, size.h ); + + dfb_rectangle_from_rotated( &src, &rect, &size, (360 - window->config.rotation) % 360 ); + + /* Blit from the window to the region being updated. */ + dfb_gfxcard_blit( &src, dest.x1, dest.y1, state ); + } + + /* Reset blitting source. */ + state->source = NULL; + state->modified |= SMF_SOURCE; +} + +static void +draw_background( CoreWindowStack *stack, CardState *state, const DFBRegion *region ) +{ + DFBRegion dest; + + D_ASSERT( stack != NULL ); + D_MAGIC_ASSERT( state, CardState ); + DFB_REGION_ASSERT( region ); + + D_ASSERT( stack->bg.image != NULL || (stack->bg.mode != DLBM_IMAGE && + stack->bg.mode != DLBM_TILE) ); + + /* Initialize destination region. */ + transform_stack_to_dest( stack, region, &dest ); + + if (!dfb_region_intersect( &dest, 0, 0, + state->destination->config.size.w - 1, state->destination->config.size.h - 1 )) + return; + + switch (stack->bg.mode) { + case DLBM_COLOR: { + DFBRectangle rect = DFB_RECTANGLE_INIT_FROM_REGION( &dest ); + CoreSurface *dst = state->destination; + DFBColor *color = &stack->bg.color; + + D_MAGIC_ASSERT( dst, CoreSurface ); + + /* Set the background color. */ + if (DFB_PIXELFORMAT_IS_INDEXED( dst->config.format )) + dfb_state_set_color_index( state, /* FIXME: don't search every time */ + dfb_palette_search( dst->palette, color->r, + color->g, color->b, color->a ) ); + else + dfb_state_set_color( state, color ); + + /* Simply fill the background. */ + dfb_gfxcard_fillrectangles( &rect, 1, state ); + break; + } + + case DLBM_IMAGE: { + CoreSurface *bg = stack->bg.image; + DFBRegion clip = state->clip; + DFBRectangle src = { 0, 0, bg->config.size.w, bg->config.size.h }; + DFBRectangle dst = { 0, 0, stack->rotated_width, stack->rotated_height }; + + D_MAGIC_ASSERT( bg, CoreSurface ); + + /* Set blitting source. */ + state->source = bg; + state->modified |= SMF_SOURCE; + + /* Set blitting flags. */ + dfb_state_set_blitting_flags( state, stack->rotated_blit ); + + /* Set clipping region. */ + dfb_state_set_clip( state, &dest ); + + /* Blit background image. */ + dfb_gfxcard_stretchblit( &src, &dst, state ); + + /* Restore clipping region. */ + dfb_state_set_clip( state, &clip ); + + /* Reset blitting source. */ + state->source = NULL; + state->modified |= SMF_SOURCE; + + break; + } + + case DLBM_TILE: { + CoreSurface *bg = stack->bg.image; + DFBRegion clip = state->clip; + DFBRectangle src = { 0, 0, bg->config.size.w, bg->config.size.h }; + + D_MAGIC_ASSERT( bg, CoreSurface ); + + /* Set blitting source. */ + state->source = bg; + state->modified |= SMF_SOURCE; + + /* Set blitting flags. */ + dfb_state_set_blitting_flags( state, stack->rotated_blit ); + + /* Change clipping region. */ + dfb_state_set_clip( state, &dest ); + + /* Tiled blit (aligned). */ + dfb_gfxcard_tileblit( &src, + (region->x1 / src.w) * src.w, + (region->y1 / src.h) * src.h, + (region->x2 / src.w + 1) * src.w, + (region->y2 / src.h + 1) * src.h, + state ); + + /* Restore clipping region. */ + dfb_state_set_clip( state, &clip ); + + /* Reset blitting source. */ + state->source = NULL; + state->modified |= SMF_SOURCE; + + break; + } + + case DLBM_DONTCARE: + break; + + default: + D_BUG( "unknown background mode" ); + break; + } +} + +static void +update_region( CoreWindowStack *stack, + StackData *data, + CardState *state, + int start, + int x1, + int y1, + int x2, + int y2 ) +{ + int i = start; + DFBRegion region = { x1, y1, x2, y2 }; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_MAGIC_ASSERT( state, CardState ); + D_ASSERT( start < fusion_vector_size( &data->windows ) ); + D_ASSERT( x1 <= x2 ); + D_ASSERT( y1 <= y2 ); + + /* Find next intersecting window. */ + while (i >= 0) { + CoreWindow *window = fusion_vector_at( &data->windows, i ); + + if (VISIBLE_WINDOW( window )) { + DFBRectangle rotated; + + transform_window_to_stack( window, &window->config.bounds, &rotated ); + + if (dfb_region_intersect( ®ion, + DFB_REGION_VALS_FROM_RECTANGLE( &rotated ))) + break; + } + + i--; + } + + /* Intersecting window found? */ + if (i >= 0) { + CoreWindow *window = fusion_vector_at( &data->windows, i ); + CoreWindowConfig *config = &window->config; + + if (D_FLAGS_ARE_SET( config->options, DWOP_ALPHACHANNEL | DWOP_OPAQUE_REGION )) { + DFBRegion opaque = DFB_REGION_INIT_TRANSLATED( &config->opaque, + config->bounds.x, + config->bounds.y ); + + if (!dfb_region_region_intersect( &opaque, ®ion )) { + update_region( stack, data, state, i-1, x1, y1, x2, y2 ); + + draw_window( window, state, ®ion, true ); + } + else { + if ((config->opacity < 0xff) || (config->options & DWOP_COLORKEYING)) { + /* draw everything below */ + update_region( stack, data, state, i-1, x1, y1, x2, y2 ); + } + else { + /* left */ + if (opaque.x1 != x1) + update_region( stack, data, state, i-1, x1, opaque.y1, opaque.x1-1, opaque.y2 ); + + /* upper */ + if (opaque.y1 != y1) + update_region( stack, data, state, i-1, x1, y1, x2, opaque.y1-1 ); + + /* right */ + if (opaque.x2 != x2) + update_region( stack, data, state, i-1, opaque.x2+1, opaque.y1, x2, opaque.y2 ); + + /* lower */ + if (opaque.y2 != y2) + update_region( stack, data, state, i-1, x1, opaque.y2+1, x2, y2 ); + } + + /* left */ + if (opaque.x1 != region.x1) { + DFBRegion r = { region.x1, opaque.y1, opaque.x1 - 1, opaque.y2 }; + draw_window( window, state, &r, true ); + } + + /* upper */ + if (opaque.y1 != region.y1) { + DFBRegion r = { region.x1, region.y1, region.x2, opaque.y1 - 1 }; + draw_window( window, state, &r, true ); + } + + /* right */ + if (opaque.x2 != region.x2) { + DFBRegion r = { opaque.x2 + 1, opaque.y1, region.x2, opaque.y2 }; + draw_window( window, state, &r, true ); + } + + /* lower */ + if (opaque.y2 != region.y2) { + DFBRegion r = { region.x1, opaque.y2 + 1, region.x2, region.y2 }; + draw_window( window, state, &r, true ); + } + + /* inner */ + draw_window( window, state, &opaque, false ); + } + } + else { + if (TRANSLUCENT_WINDOW( window )) { + /* draw everything below */ + update_region( stack, data, state, i-1, x1, y1, x2, y2 ); + } + else { + /* left */ + if (region.x1 != x1) + update_region( stack, data, state, i-1, x1, region.y1, region.x1-1, region.y2 ); + + /* upper */ + if (region.y1 != y1) + update_region( stack, data, state, i-1, x1, y1, x2, region.y1-1 ); + + /* right */ + if (region.x2 != x2) + update_region( stack, data, state, i-1, region.x2+1, region.y1, x2, region.y2 ); + + /* lower */ + if (region.y2 != y2) + update_region( stack, data, state, i-1, x1, region.y2+1, x2, y2 ); + } + + draw_window( window, state, ®ion, true ); + } + } + else + draw_background( stack, state, ®ion ); +} + +/**************************************************************************************************/ +/**************************************************************************************************/ + +static void +repaint_stack( CoreWindowStack *stack, + StackData *data, + CoreLayerRegion *region, + const DFBRegion *updates, + int num_updates, + DFBSurfaceFlipFlags flags ) +{ + int i; + CoreLayer *layer; + CardState *state; + CoreSurface *surface; + DFBRegion flips[num_updates]; + int num_flips = 0; + + D_ASSERT( stack != NULL ); + D_ASSERT( stack->context != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( region != NULL ); + D_ASSERT( num_updates > 0 ); + + layer = dfb_layer_at( stack->context->layer_id ); + state = &layer->state; + surface = region->surface; + + if (!data->active || !surface) + return; + + D_DEBUG_AT( WM_Default, "repaint_stack( %d region(s), flags %x )\n", num_updates, flags ); + + /* Set destination. */ + state->destination = surface; + state->modified |= SMF_DESTINATION; + + for (i=0; i %d, %d - %dx%d (%d)\n", + DFB_RECTANGLE_VALS_FROM_REGION( update ), i ); + + transform_stack_to_dest( stack, update, &dest ); + + if (!dfb_region_intersect( &dest, 0, 0, surface->config.size.w - 1, surface->config.size.h - 1 )) + continue; + + /* Set clipping region. */ + dfb_state_set_clip( state, &dest ); + + /* Compose updated region. */ + update_region( stack, data, state, + fusion_vector_size( &data->windows ) - 1, + DFB_REGION_VALS( update ) ); + + flips[num_flips++] = dest; + + /* Update cursor? */ + if (data->cursor_drawn) { + DFBRegion cursor_rotated; + + D_ASSUME( data->cursor_bs_valid ); + + transform_stack_to_dest( stack, &data->cursor_region, &cursor_rotated ); + + if (dfb_region_region_intersect( &dest, &cursor_rotated )) { + DFBRectangle rect = DFB_RECTANGLE_INIT_FROM_REGION( &dest ); + + dfb_gfx_copy_to( surface, data->cursor_bs, &rect, + rect.x - cursor_rotated.x1, + rect.y - cursor_rotated.y1, true ); + + draw_cursor( stack, data, state, &data->cursor_region ); + } + } + } + + /* Reset destination. */ + state->destination = NULL; + state->modified |= SMF_DESTINATION; + + /* Software cursor code relies on a valid back buffer. */ + if (stack->cursor.enabled) + flags |= DSFLIP_BLIT; + + for (i=0; icontext; + D_ASSERT( context != NULL ); + + if (!data->updates.num_regions) + return DFB_OK; + + /* Get the primary region. */ + if (!region) { + ret = dfb_layer_context_get_primary_region( stack->context, false, &primary ); + if (ret) + return ret; + } + + + dfb_updates_stat( &data->updates, &total, &bounding ); + + n = data->updates.max_regions - data->updates.num_regions + 1; + d = n + 1; + + /* FIXME: depend on buffer mode, hw accel etc. */ + if (total > stack->width * stack->height * 9 / 10) { + DFBRegion region = { 0, 0, stack->width - 1, stack->height - 1 }; + +// direct_log_printf( NULL, "%s() <- %d regions, total %d, bounding %d (%d/%d: %d), FULL UPDATE\n", +// __FUNCTION__, data->updates.num_regions, total, bounding, n, d, bounding*n/d ); + +// if (context->config.buffermode == DLBM_FRONTONLY) +// dfb_region_transpose(®ion, context->rotation); + + repaint_stack( stack, data, primary, ®ion, 1, flags ); + } + else if (data->updates.num_regions < 2 || total < bounding * n / d) + repaint_stack( stack, data, primary, data->updates.regions, data->updates.num_regions, flags ); + else { +// direct_log_printf( NULL, "%s() <- %d regions, total %d, bounding %d (%d/%d: %d)\n", +// __FUNCTION__, data->updates.num_regions, total, bounding, n, d, bounding*n/d ); + + repaint_stack( stack, data, primary, &data->updates.bounding, 1, flags ); + } + + + dfb_updates_reset( &data->updates ); + + /* Unref primary region. */ + if (!region) + dfb_layer_region_unref( primary ); + + return DFB_OK; +} + +/* + skipping opaque windows that are above the window that changed +*/ +static void +wind_of_change( CoreWindowStack *stack, + StackData *data, + CoreLayerRegion *region, + DFBRegion *update, + DFBSurfaceFlipFlags flags, + int current, + int changed ) +{ + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( region != NULL ); + D_ASSERT( update != NULL ); + + /* + loop through windows above + */ + for (; current > changed; current--) { + CoreWindow *window; + CoreWindowConfig *config; + DFBRegion opaque; + DFBRectangle rotated; + DFBRectangle *bounds = &rotated; + DFBWindowOptions options; + + D_ASSERT( changed >= 0 ); + D_ASSERT( current >= changed ); + D_ASSERT( current < fusion_vector_size( &data->windows ) ); + + window = fusion_vector_at( &data->windows, current ); + config = &window->config; + options = config->options; + + transform_window_to_stack( window, &config->bounds, &rotated ); + + /* + can skip opaque region + */ + if (( + //can skip all opaque window? + (config->opacity == 0xff) && + !(options & (DWOP_COLORKEYING | DWOP_ALPHACHANNEL)) && + (opaque=*update,dfb_region_intersect( &opaque, + bounds->x, bounds->y, + bounds->x + bounds->w - 1, + bounds->y + bounds->h -1 ) ) + )||( + //can skip opaque region? + (options & DWOP_ALPHACHANNEL) && + (options & DWOP_OPAQUE_REGION) && + (config->opacity == 0xff) && + !(options & DWOP_COLORKEYING) && + (opaque=*update,dfb_region_intersect( &opaque, + bounds->x + config->opaque.x1, + bounds->y + config->opaque.y1, + bounds->x + config->opaque.x2, + bounds->y + config->opaque.y2 )) + )) + { + /* left */ + if (opaque.x1 != update->x1) { + DFBRegion left = { update->x1, opaque.y1, opaque.x1-1, opaque.y2}; + wind_of_change( stack, data, region, &left, flags, current-1, changed ); + } + /* upper */ + if (opaque.y1 != update->y1) { + DFBRegion upper = { update->x1, update->y1, update->x2, opaque.y1-1}; + wind_of_change( stack, data, region, &upper, flags, current-1, changed ); + } + /* right */ + if (opaque.x2 != update->x2) { + DFBRegion right = { opaque.x2+1, opaque.y1, update->x2, opaque.y2}; + wind_of_change( stack, data, region, &right, flags, current-1, changed ); + } + /* lower */ + if (opaque.y2 != update->y2) { + DFBRegion lower = { update->x1, opaque.y2+1, update->x2, update->y2}; + wind_of_change( stack, data, region, &lower, flags, current-1, changed ); + } + + return; + } + } + + dfb_updates_add( &data->updates, update ); +} + +static void +repaint_stack_for_window( CoreWindowStack *stack, + StackData *data, + CoreLayerRegion *region, + DFBRegion *update, + DFBSurfaceFlipFlags flags, + int window ) +{ + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( region != NULL ); + D_ASSERT( update != NULL ); + D_ASSERT( window >= 0 ); + D_ASSERT( window < fusion_vector_size( &data->windows ) ); + + if (fusion_vector_has_elements( &data->windows ) && window >= 0) { + int num = fusion_vector_size( &data->windows ); + + D_ASSERT( window < num ); + + wind_of_change( stack, data, region, update, flags, num - 1, window ); + } + else + dfb_updates_add( &data->updates, update ); +} + +/**************************************************************************************************/ + +static DFBResult +update_window( CoreWindow *window, + WindowData *window_data, + const DFBRegion *region, + DFBSurfaceFlipFlags flags, + bool force_complete, + bool force_invisible, + bool scale_region ) +{ + DFBRegion area; + DFBRegion update; + StackData *data; + CoreWindowStack *stack; + DFBRectangle *bounds; + DFBDimension size; + + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( window_data->stack_data != NULL ); + D_ASSERT( window_data->stack_data->stack != NULL ); + + DFB_REGION_ASSERT_IF( region ); + + data = window_data->stack_data; + stack = data->stack; + + if (!VISIBLE_WINDOW(window) && !force_invisible) + return DFB_OK; + + if (stack->hw_mode) + return DFB_OK; + + bounds = &window->config.bounds; + size.w = bounds->w; + size.h = bounds->h; + + if (region) { + if (scale_region && (window->config.options & DWOP_SCALE)) { + int sw = window->surface->config.size.w; + int sh = window->surface->config.size.h; + + /* horizontal */ + if (bounds->w > sw) { + /* upscaling */ + area.x1 = (region->x1 - 1) * bounds->w / sw; + area.x2 = (region->x2 + 1) * bounds->w / sw; + } + else { + /* downscaling */ + area.x1 = region->x1 * bounds->w / sw - 1; + area.x2 = region->x2 * bounds->w / sw + 1; + } + + /* vertical */ + if (bounds->h > sh) { + /* upscaling */ + area.y1 = (region->y1 - 1) * bounds->h / sh; + area.y2 = (region->y2 + 1) * bounds->h / sh; + } + else { + /* downscaling */ + area.y1 = region->y1 * bounds->h / sh - 1; + area.y2 = region->y2 * bounds->h / sh + 1; + } + + /* limit to window area */ + dfb_region_clip( &area, 0, 0, bounds->w - 1, bounds->h - 1 ); + } + else + area = *region; + } + else { + area.x1 = area.y1 = 0; + area.x2 = bounds->w - 1; + area.y2 = bounds->h - 1; + } + + dfb_region_from_rotated( &update, &area, &size, window->config.rotation ); + + /* screen offset */ + dfb_region_translate( &update, bounds->x, bounds->y ); + + if (!dfb_unsafe_region_intersect( &update, 0, 0, stack->width - 1, stack->height - 1 )) + return DFB_OK; + + if (force_complete) + dfb_updates_add( &data->updates, &update ); + else + repaint_stack_for_window( stack, data, window->primary_region, + &update, flags, get_index( data, window ) ); + + return DFB_OK; +} + +/**************************************************************************************************/ +/**************************************************************************************************/ + +static void +insert_window( CoreWindowStack *stack, + StackData *data, + CoreWindow *window, + WindowData *window_data ) +{ + int index; + CoreWindow *other; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + + /* + * Iterate from bottom to top, + * stopping at the first window with a higher priority. + */ + fusion_vector_foreach (other, index, data->windows) { + WindowData *other_data = other->window_data; + + D_ASSERT( other->window_data != NULL ); + + if (other_data->priority > window_data->priority) + break; + } + + /* Insert the window at the acquired position. */ + fusion_vector_insert( &data->windows, window, index ); +} + +static void +withdraw_window( CoreWindowStack *stack, + StackData *data, + CoreWindow *window, + WindowData *window_data ) +{ + int i; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + + D_ASSERT( window->stack != NULL ); + + D_ASSERT( DFB_WINDOW_INITIALIZED( window ) ); + + /* No longer be the 'entered window'. */ + if (data->entered_window == window) + data->entered_window = NULL; + + /* Remove focus from window. */ + if (data->focused_window == window) + data->focused_window = NULL; + + /* Release explicit keyboard grab. */ + if (data->keyboard_window == window) + data->keyboard_window = NULL; + + /* Release explicit pointer grab. */ + if (data->pointer_window == window) + data->pointer_window = NULL; + + /* Release all implicit key grabs. */ + for (i=0; ikeys[i].code != -1 && data->keys[i].owner == window) { + if (!DFB_WINDOW_DESTROYED( window )) { + DFBWindowEvent we; + + we.type = DWET_KEYUP; + we.key_code = data->keys[i].code; + we.key_id = data->keys[i].id; + we.key_symbol = data->keys[i].symbol; + + post_event( window, data, &we ); + } + + data->keys[i].code = -1; + data->keys[i].owner = NULL; + } + } + + /* Release grab of unselected keys. */ + if (data->unselkeys_window == window) + data->unselkeys_window = NULL; +} + +static void +remove_window( CoreWindowStack *stack, + StackData *data, + CoreWindow *window, + WindowData *window_data ) +{ + DirectLink *l, *n; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + + D_ASSERT( window->config.opacity == 0 ); + D_ASSERT( DFB_WINDOW_INITIALIZED( window ) ); + + D_ASSERT( fusion_vector_contains( &data->windows, window ) ); + + /* Release implicit grabs, focus etc. */ + withdraw_window( stack, data, window, window_data ); + + /* Release all explicit key grabs. */ + direct_list_foreach_safe (l, n, data->grabbed_keys) { + GrabbedKey *key = (GrabbedKey*) l; + + if (key->owner == window) { + direct_list_remove( &data->grabbed_keys, &key->link ); + SHFREE( stack->shmpool, key ); + } + } + + fusion_vector_remove( &data->windows, fusion_vector_index_of( &data->windows, window ) ); +} + +/**************************************************************************************************/ + +static DFBResult +move_window( CoreWindow *window, + WindowData *data, + int dx, + int dy ) +{ + DFBResult ret; + DFBWindowEvent evt; + DFBRectangle *bounds = &window->config.bounds; + + if (window->region) { + data->config.dest.x += dx; + data->config.dest.y += dy; + + ret = dfb_layer_region_set_configuration( window->region, &data->config, CLRCF_DEST ); + if (ret) { + data->config.dest.x -= dx; + data->config.dest.y -= dy; + + return ret; + } + + bounds->x += dx; + bounds->y += dy; + } + else { + update_window( window, data, NULL, 0, false, false, false ); + + bounds->x += dx; + bounds->y += dy; + + update_window( window, data, NULL, 0, false, false, false ); + } + + /* Send new position */ + evt.type = DWET_POSITION; + evt.x = bounds->x; + evt.y = bounds->y; + + post_event( window, data->stack_data, &evt ); + + return DFB_OK; +} + +static DFBResult +resize_window( CoreWindow *window, + WMData *wm_data, + WindowData *data, + int width, + int height ) +{ + DFBResult ret; + DFBWindowEvent evt; + CoreWindowStack *stack = window->stack; + DFBRectangle *bounds = &window->config.bounds; + int ow = bounds->w; + int oh = bounds->h; + + D_DEBUG_AT( WM_Default, "resize_window( %d, %d )\n", width, height ); + + D_ASSERT( wm_data != NULL ); + + D_MAGIC_ASSERT( data, WindowData ); + + D_ASSERT( width > 0 ); + D_ASSERT( height > 0 ); + + if (width > 4096 || height > 4096) + return DFB_LIMITEXCEEDED; + + if (window->surface && !(window->config.options & DWOP_SCALE)) { + CoreSurfaceConfig config; + + config.flags = CSCONF_SIZE; + config.size.w = width; + config.size.h = height; + + ret = dfb_surface_reconfig( window->surface, &config ); + if (ret) + return ret; + } + + if (window->region) { + data->config.dest.w = data->config.source.w = data->config.width = width; + data->config.dest.h = data->config.source.h = data->config.height = height; + + ret = dfb_layer_region_set_configuration( window->region, &data->config, + CLRCF_WIDTH | CLRCF_HEIGHT | CLRCF_SURFACE | + CLRCF_DEST | CLRCF_SOURCE ); + if (ret) { + data->config.dest.w = data->config.source.w = data->config.width = bounds->w = ow; + data->config.dest.h = data->config.source.h = data->config.height = bounds->h = oh; + + return ret; + } + } + else { + dfb_region_intersect( &window->config.opaque, 0, 0, width - 1, height - 1 ); + + if (VISIBLE_WINDOW (window)) { + if (ow > width) { + DFBRegion region = { width, 0, ow - 1, MIN(height, oh) - 1 }; + + update_window( window, data, ®ion, 0, false, false, false ); + } + + if (oh > height) { + DFBRegion region = { 0, height, MAX(width, ow) - 1, oh - 1 }; + + update_window( window, data, ®ion, 0, false, false, false ); + } + } + } + + bounds->w = width; + bounds->h = height; + + /* Send new size */ + evt.type = DWET_SIZE; + evt.w = bounds->w; + evt.h = bounds->h; + + post_event( window, data->stack_data, &evt ); + + update_focus( stack, data->stack_data, wm_data ); + + return DFB_OK; +} + +static DFBResult +set_window_bounds( CoreWindow *window, + WMData *wm_data, + WindowData *data, + int x, + int y, + int width, + int height) +{ + DFBResult ret; + DFBWindowEvent evt; + CoreWindowStack *stack = window->stack; + DFBRegion old_region; + DFBRegion new_region; + + D_DEBUG_AT( WM_Default, "%s( %p [%d] %d, %d - %dx%d )\n", __FUNCTION__, window, window->id, x, y, width, height ); + + D_ASSERT( wm_data != NULL ); + + D_MAGIC_ASSERT( data, WindowData ); + + D_ASSERT( width > 0 ); + D_ASSERT( height > 0 ); + + if (width > 4096 || height > 4096) + return DFB_LIMITEXCEEDED; + + if (window->surface && !(window->config.options & DWOP_SCALE)) { + ret = dfb_surface_reformat( window->surface, + width, height, window->surface->config.format ); + if (ret) + return ret; + } + + old_region.x1 = window->config.bounds.x - x; + old_region.y1 = window->config.bounds.y - y; + old_region.x2 = old_region.x1 + window->config.bounds.w - 1; + old_region.y2 = old_region.y1 + window->config.bounds.h - 1; + + window->config.bounds.x = x; + window->config.bounds.y = y; + window->config.bounds.w = width; + window->config.bounds.h = height; + + new_region.x1 = 0; + new_region.y1 = 0; + new_region.x2 = width - 1; + new_region.y2 = height - 1; + + if (!dfb_region_region_intersect( &window->config.opaque, &new_region )) + window->config.opaque = new_region; + + /* Update exposed area. */ + if (VISIBLE_WINDOW( window )) { + if (dfb_region_region_intersect( &new_region, &old_region )) { + /* left */ + if (new_region.x1 > old_region.x1) { + DFBRegion region = { old_region.x1, old_region.y1, + new_region.x1 - 1, new_region.y2 }; + + update_window( window, data, ®ion, 0, false, false, false ); + } + + /* upper */ + if (new_region.y1 > old_region.y1) { + DFBRegion region = { old_region.x1, old_region.y1, + old_region.x2, new_region.y1 - 1 }; + + update_window( window, data, ®ion, 0, false, false, false ); + } + + /* right */ + if (new_region.x2 < old_region.x2) { + DFBRegion region = { new_region.x2 + 1, new_region.y1, + old_region.x2, new_region.y2 }; + + update_window( window, data, ®ion, 0, false, false, false ); + } + + /* lower */ + if (new_region.y2 < old_region.y2) { + DFBRegion region = { old_region.x1, new_region.y2 + 1, + old_region.x2, old_region.y2 }; + + update_window( window, data, ®ion, 0, false, false, false ); + } + } + else + update_window( window, data, &old_region, 0, false, false, false ); + } + + /* Send new position and size */ + evt.type = DWET_POSITION_SIZE; + evt.x = window->config.bounds.x; + evt.y = window->config.bounds.y; + evt.w = window->config.bounds.w; + evt.h = window->config.bounds.h; + + post_event( window, data->stack_data, &evt ); + + update_focus( stack, data->stack_data, wm_data ); + + return DFB_OK; +} + +static DFBResult +restack_window( CoreWindow *window, + WindowData *window_data, + CoreWindow *relative, + WindowData *relative_data, + int relation, + DFBWindowStackingClass stacking ) +{ + StackData *data; + int old; + int index; + int priority; + + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( window_data->stack_data != NULL ); + + D_ASSERT( relative == NULL || relative_data != NULL ); + + D_ASSERT( relative == NULL || relative == window || relation != 0); + + data = window_data->stack_data; + + /* Change stacking class. */ + if (stacking != window->config.stacking) { + window->config.stacking = stacking; + + window_data->priority = get_priority( window ); + } + + /* Get the (new) priority. */ + priority = window_data->priority; + + /* Get the old index. */ + old = get_index( data, window ); + + /* Calculate the desired index. */ + if (relative) { + index = get_index( data, relative ); + + if (relation > 0) { + if (old < index) + index--; + } + else if (relation < 0) { + if (old > index) + index++; + } + + index += relation; + + if (index < 0) + index = 0; + else if (index > fusion_vector_size( &data->windows ) - 1) + index = fusion_vector_size( &data->windows ) - 1; + } + else if (relation) + index = fusion_vector_size( &data->windows ) - 1; + else + index = 0; + + /* Assure window won't be above any window with a higher priority. */ + while (index > 0) { + int below = (old < index) ? index : index - 1; + CoreWindow *other = fusion_vector_at( &data->windows, below ); + WindowData *other_data = other->window_data; + + D_ASSERT( other->window_data != NULL ); + + if (priority < other_data->priority) + index--; + else + break; + } + + /* Assure window won't be below any window with a lower priority. */ + while (index < fusion_vector_size( &data->windows ) - 1) { + int above = (old > index) ? index : index + 1; + CoreWindow *other = fusion_vector_at( &data->windows, above ); + WindowData *other_data = other->window_data; + + D_ASSERT( other->window_data != NULL ); + + if (priority > other_data->priority) + index++; + else + break; + } + + /* Return if index hasn't changed. */ + if (index == old) + return DFB_OK; + + /* Actually change the stacking order now. */ + fusion_vector_move( &data->windows, old, index ); + + update_window( window, window_data, NULL, DSFLIP_NONE, (index < old), false, false ); + + return DFB_OK; +} + +static void +set_opacity( CoreWindow *window, + WindowData *window_data, + WMData *wmdata, + u8 opacity ) +{ + u8 old; + StackData *data; + CoreWindowStack *stack; + + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( window_data->stack_data != NULL ); + D_ASSERT( window_data->stack_data->stack != NULL ); + + old = window->config.opacity; + data = window_data->stack_data; + stack = data->stack; + + if (!stack->hw_mode && !dfb_config->translucent_windows && opacity) + opacity = 0xFF; + + if (old != opacity) { + bool show = !old && opacity; + bool hide = old && !opacity; + + window->config.opacity = opacity; + + if (window->region) { + window_data->config.opacity = opacity; + + dfb_layer_region_set_configuration( window->region, &window_data->config, CLRCF_OPACITY ); + } + else + update_window( window, window_data, NULL, DSFLIP_NONE, false, true, false ); + + + /* Check focus after window appeared or disappeared */ + if (show || hide) + update_focus( stack, data, wmdata ); + + /* If window disappeared... */ + if (hide) { + /* Ungrab pointer/keyboard */ + withdraw_window( stack, data, window, window_data ); + + /* Always try to have a focused window */ + ensure_focus( stack, data ); + } + } +} + +static DFBResult +grab_keyboard( CoreWindow *window, + WindowData *window_data ) +{ + StackData *data; + + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( window_data->stack_data != NULL ); + + data = window_data->stack_data; + + if (data->keyboard_window) + return DFB_LOCKED; + + data->keyboard_window = window; + + return DFB_OK; +} + +static DFBResult +ungrab_keyboard( CoreWindow *window, + WindowData *window_data ) +{ + StackData *data; + + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( window_data->stack_data != NULL ); + + data = window_data->stack_data; + + if (data->keyboard_window == window) + data->keyboard_window = NULL; + + return DFB_OK; +} + +static DFBResult +grab_pointer( CoreWindow *window, + WindowData *window_data ) +{ + StackData *data; + + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( window_data->stack_data != NULL ); + + data = window_data->stack_data; + + if (data->pointer_window) + return DFB_LOCKED; + + data->pointer_window = window; + + return DFB_OK; +} + +static DFBResult +ungrab_pointer( CoreWindow *window, + WindowData *window_data, + WMData *wmdata ) +{ + StackData *data; + CoreWindowStack *stack; + + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( window_data->stack_data != NULL ); + + data = window_data->stack_data; + stack = data->stack; + + if (data->pointer_window == window) { + data->pointer_window = NULL; + + /* Possibly change focus to window that's now under the cursor */ + update_focus( stack, data, wmdata ); + } + + return DFB_OK; +} + +static DFBResult +grab_key( CoreWindow *window, + WindowData *window_data, + DFBInputDeviceKeySymbol symbol, + DFBInputDeviceModifierMask modifiers ) +{ + int i; + StackData *data; + GrabbedKey *grab; + CoreWindowStack *stack; + + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( window_data->stack_data != NULL ); + D_ASSERT( window_data->stack_data->stack != NULL ); + + data = window_data->stack_data; + stack = data->stack; + + /* Reject if already grabbed. */ + direct_list_foreach (grab, data->grabbed_keys) + if (grab->symbol == symbol && grab->modifiers == modifiers) + return DFB_LOCKED; + + /* Allocate grab information. */ + grab = SHCALLOC( stack->shmpool, 1, sizeof(GrabbedKey) ); + + /* Fill grab information. */ + grab->symbol = symbol; + grab->modifiers = modifiers; + grab->owner = window; + + /* Add to list of key grabs. */ + direct_list_append( &data->grabbed_keys, &grab->link ); + + /* Remove implicit grabs for this key. */ + for (i=0; ikeys[i].code != -1 && data->keys[i].symbol == symbol) + data->keys[i].code = -1; + + return DFB_OK; +} + +static DFBResult +ungrab_key( CoreWindow *window, + WindowData *window_data, + DFBInputDeviceKeySymbol symbol, + DFBInputDeviceModifierMask modifiers ) +{ + DirectLink *l; + StackData *data; + CoreWindowStack *stack; + + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( window_data->stack_data != NULL ); + D_ASSERT( window_data->stack_data->stack != NULL ); + + data = window_data->stack_data; + stack = data->stack; + + direct_list_foreach (l, data->grabbed_keys) { + GrabbedKey *key = (GrabbedKey*) l; + + if (key->symbol == symbol && key->modifiers == modifiers && key->owner == window) { + direct_list_remove( &data->grabbed_keys, &key->link ); + SHFREE( stack->shmpool, key ); + return DFB_OK; + } + } + + return DFB_IDNOTFOUND; +} + +static DFBResult +request_focus( CoreWindow *window, + WindowData *window_data ) +{ + StackData *data; + CoreWindowStack *stack; + CoreWindow *entered; + + D_ASSERT( window != NULL ); + D_ASSERT( !(window->config.options & DWOP_GHOST) ); + D_ASSERT( window_data != NULL ); + D_ASSERT( window_data->stack_data != NULL ); + + data = window_data->stack_data; + stack = data->stack; + + switch_focus( stack, data, window ); + + entered = data->entered_window; + + if (entered && entered != window) { + DFBWindowEvent we; + + we.type = DWET_LEAVE; + we.x = stack->cursor.x - entered->config.bounds.x; + we.y = stack->cursor.y - entered->config.bounds.y; + + transform_point_in_window( entered, &we.x, &we.y ); + + post_event( entered, data, &we ); + + data->entered_window = NULL; + } + + return DFB_OK; +} + +/**************************************************************************************************/ +/**************************************************************************************************/ + +static bool +handle_wm_key( CoreWindowStack *stack, + StackData *data, + WMData *wmdata, + const DFBInputEvent *event ) +{ + int i, num; + CoreWindow *entered; + CoreWindow *focused; + CoreWindow *window; + DFBRegion region; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( data->wm_level > 0 ); + D_ASSERT( event != NULL ); + D_ASSERT( event->type == DIET_KEYPRESS ); + + entered = data->entered_window; + focused = data->focused_window; + + switch (DFB_LOWER_CASE(event->key_symbol)) { + case DIKS_SMALL_X: + num = fusion_vector_size( &data->windows ); + + if (data->wm_cycle <= 0) + data->wm_cycle = num; + + if (num) { + int looped = 0; + int index = MIN( num, data->wm_cycle ); + + while (index--) { + CoreWindow *window = fusion_vector_at( &data->windows, index ); + + if ((window->config.options & (DWOP_GHOST | DWOP_KEEP_STACKING)) || + ! VISIBLE_WINDOW(window) || window == data->focused_window) + { + if (index == 0 && !looped) { + looped = 1; + index = num - 1; + } + + continue; + } + + restack_window( window, window->window_data, + NULL, NULL, 1, window->config.stacking ); + request_focus( window, window->window_data ); + + break; + } + + data->wm_cycle = index; + } + break; + + case DIKS_SMALL_S: + fusion_vector_foreach (window, i, data->windows) { + if (VISIBLE_WINDOW(window) && window->config.stacking == DWSC_MIDDLE && + ! (window->config.options & (DWOP_GHOST | DWOP_KEEP_STACKING))) + { + restack_window( window, window->window_data, + NULL, NULL, 1, window->config.stacking ); + request_focus( window, window->window_data ); + + break; + } + } + break; + + case DIKS_SMALL_C: + if (entered) { + DFBWindowEvent event; + + event.type = DWET_CLOSE; + + post_event( entered, data, &event ); + } + break; + + case DIKS_SMALL_E: + update_focus( stack, data, wmdata ); + break; + + case DIKS_SMALL_A: + if (focused && !(focused->config.options & DWOP_KEEP_STACKING)) { + restack_window( focused, focused->window_data, + NULL, NULL, 0, focused->config.stacking ); + update_focus( stack, data, wmdata ); + } + break; + + case DIKS_SMALL_W: + if (focused && !(focused->config.options & DWOP_KEEP_STACKING)) + restack_window( focused, focused->window_data, + NULL, NULL, 1, focused->config.stacking ); + break; + + case DIKS_SMALL_D: + if (entered && !(entered->config.options & DWOP_INDESTRUCTIBLE)) + dfb_window_destroy( entered ); + + break; + + case DIKS_SMALL_P: + /* Enable and show cursor. */ + if (stack->cursor.set) { + dfb_windowstack_cursor_set_opacity( stack, 0xff ); + dfb_windowstack_cursor_enable( wmdata->core, stack, true ); + } + + /* Ungrab pointer. */ + data->pointer_window = NULL; + + /* TODO: set new cursor shape, the current one might be completely transparent */ + break; + + case DIKS_SMALL_R: + if (focused && !(focused->config.options & DWOP_KEEP_POSITION)) + dfb_window_set_rotation( focused, (focused->config.rotation + 90) % 360 ); + break; + + case DIKS_PRINT: + if (dfb_config->screenshot_dir && focused && focused->surface) + dfb_surface_dump_buffer( focused->surface, CSBR_FRONT, dfb_config->screenshot_dir, "dfb_window" ); + break; + + case DIKS_F12: + region.x1 = 0; + region.y1 = 0; + region.x2 = stack->width; + region.y2 = stack->height; + + dfb_updates_reset( &data->updates ); + dfb_updates_add( &data->updates, ®ion ); + break; + + default: + return false; + } + + return true; +} + +static bool +is_wm_key( DFBInputDeviceKeySymbol key_symbol ) +{ + switch (DFB_LOWER_CASE(key_symbol)) { + case DIKS_SMALL_X: + case DIKS_SMALL_S: + case DIKS_SMALL_C: + case DIKS_SMALL_E: + case DIKS_SMALL_A: + case DIKS_SMALL_W: + case DIKS_SMALL_D: + case DIKS_SMALL_P: + case DIKS_SMALL_R: + case DIKS_PRINT: + break; + + default: + return false; + } + + return true; +} + + +/**************************************************************************************************/ + +static DFBResult +handle_key_press( CoreWindowStack *stack, + StackData *data, + WMData *wmdata, + const DFBInputEvent *event ) +{ + CoreWindow *window; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( event != NULL ); + D_ASSERT( event->type == DIET_KEYPRESS ); + + if (data->wm_level) { + switch (event->key_symbol) { + case DIKS_META: + data->wm_level |= 1; + break; + + case DIKS_CONTROL: + data->wm_level |= 2; + break; + + case DIKS_ALT: + data->wm_level |= 4; + break; + + default: + if (handle_wm_key( stack, data, wmdata, event )) + return DFB_OK; + + break; + } + } + else if (event->key_symbol == DIKS_META) { + data->wm_level |= 1; + data->wm_cycle = 0; + } + + window = get_keyboard_window( stack, data, event ); + if (window) + send_key_event( window, data, event ); + + return DFB_OK; +} + +static DFBResult +handle_key_release( CoreWindowStack *stack, + StackData *data, + const DFBInputEvent *event ) +{ + CoreWindow *window; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( event != NULL ); + D_ASSERT( event->type == DIET_KEYRELEASE ); + + if (data->wm_level) { + switch (event->key_symbol) { + case DIKS_META: + data->wm_level &= ~1; + break; + + case DIKS_CONTROL: + data->wm_level &= ~2; + break; + + case DIKS_ALT: + data->wm_level &= ~4; + break; + + default: + if (is_wm_key( event->key_symbol )) + return DFB_OK; + + break; + } + } + + window = get_keyboard_window( stack, data, event ); + if (window) + send_key_event( window, data, event ); + + return DFB_OK; +} + +/**************************************************************************************************/ + +static DFBResult +handle_button_press( CoreWindowStack *stack, + StackData *data, + const DFBInputEvent *event ) +{ + CoreWindow *window; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( event != NULL ); + D_ASSERT( event->type == DIET_BUTTONPRESS ); + + if (!stack->cursor.enabled) + return DFB_OK; + + switch (data->wm_level) { + case 1: + window = data->entered_window; + if (window && !(window->config.options & DWOP_KEEP_STACKING)) + dfb_window_raisetotop( data->entered_window ); + + break; + + default: + window = data->pointer_window ? data->pointer_window : data->entered_window; + if (window) + send_button_event( window, data, event ); + + break; + } + + return DFB_OK; +} + +static DFBResult +handle_button_release( CoreWindowStack *stack, + StackData *data, + const DFBInputEvent *event ) +{ + CoreWindow *window; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( event != NULL ); + D_ASSERT( event->type == DIET_BUTTONRELEASE ); + + if (!stack->cursor.enabled) + return DFB_OK; + + switch (data->wm_level) { + case 1: + break; + + default: + window = data->pointer_window ? data->pointer_window : data->entered_window; + if (window) + send_button_event( window, data, event ); + + break; + } + + return DFB_OK; +} + +/**************************************************************************************************/ + +static void +perform_motion( CoreWindowStack *stack, + StackData *data, + WMData *wmdata, + int dx, + int dy ) +{ + int old_cx, old_cy; + DFBWindowEvent we; + CoreWindow *entered; + CoreWindowConfig *config = NULL; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + + if (!stack->cursor.enabled) + return; + + + old_cx = stack->cursor.x; + old_cy = stack->cursor.y; + + dfb_windowstack_cursor_warp( stack, old_cx + dx, old_cy + dy ); + + dx = stack->cursor.x - old_cx; + dy = stack->cursor.y - old_cy; + + if (!dx && !dy) + return; + + + entered = data->entered_window; + if (entered) + config = &entered->config; + + switch (data->wm_level) { + case 7: + case 6: + case 5: + case 4: + if (entered) { + int opacity = config->opacity + dx; + + if (opacity < 8) + opacity = 8; + else if (opacity > 255) + opacity = 255; + + dfb_window_set_opacity( entered, opacity ); + } + + break; + + case 3: + case 2: + if (entered && !(config->options & DWOP_KEEP_SIZE)) { + int width = config->bounds.w + dx; + int height = config->bounds.h + dy; + + if (width < 48) width = 48; + if (height < 48) height = 48; + if (width > 2048) width = 2048; + if (height > 2048) height = 2048; + + dfb_window_resize( entered, width, height ); + } + + break; + + case 1: + if (entered && !(config->options & DWOP_KEEP_POSITION)) + dfb_window_move( entered, dx, dy, true ); + + break; + + case 0: + if (data->pointer_window) { + CoreWindow *window = data->pointer_window; + + we.type = DWET_MOTION; + we.x = stack->cursor.x - window->config.bounds.x; + we.y = stack->cursor.y - window->config.bounds.y; + + transform_point_in_window( window, &we.x, &we.y ); + + post_event( window, data, &we ); + } + else if (!update_focus( stack, data, wmdata ) && data->entered_window) { + CoreWindow *window = data->entered_window; + + we.type = DWET_MOTION; + we.x = stack->cursor.x - window->config.bounds.x; + we.y = stack->cursor.y - window->config.bounds.y; + + transform_point_in_window( window, &we.x, &we.y ); + + post_event( window, data, &we ); + } + + break; + + default: + ; + } +} + +static void +flush_motion( CoreWindowStack *stack, + StackData *data, + WMData *wmdata ) +{ + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( wmdata != NULL ); + + if (data->cursor_dx || data->cursor_dy) { + perform_motion( stack, data, wmdata, data->cursor_dx, data->cursor_dy ); + + data->cursor_dx = 0; + data->cursor_dy = 0; + } +} + +static void +handle_wheel( CoreWindowStack *stack, + StackData *data, + int dz ) +{ + DFBWindowEvent we; + CoreWindow *window = NULL; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + + if (!stack->cursor.enabled) + return; + + window = data->pointer_window ? data->pointer_window : data->entered_window; + + if (window) { + if (data->wm_level) { + int opacity = window->config.opacity + dz*7; + + if (opacity < 0x01) + opacity = 1; + if (opacity > 0xFF) + opacity = 0xFF; + + dfb_window_set_opacity( window, opacity ); + } + else { + we.type = DWET_WHEEL; + we.x = stack->cursor.x - window->config.bounds.x; + we.y = stack->cursor.y - window->config.bounds.y; + we.step = dz; + + transform_point_in_window( window, &we.x, &we.y ); + + post_event( window, data, &we ); + } + } +} + +static DFBResult +handle_axis_motion( CoreWindowStack *stack, + StackData *data, + WMData *wmdata, + const DFBInputEvent *event ) +{ + CoreLayerContext *context; + DFBInputEvent rotated_event; + + D_ASSERT( stack != NULL ); + D_ASSERT( data != NULL ); + D_ASSERT( event != NULL ); + D_ASSERT( event->type == DIET_AXISMOTION ); + + context = stack->context; + D_MAGIC_ASSERT( context, CoreLayerContext ); + + if (event->flags & DIEF_AXISREL) { + rotated_event = *event; + event = &rotated_event; + + if (event->axis == DIAI_X) { + if (context->rotation == 90) { + rotated_event.axis = DIAI_Y; + } + else if (context->rotation == 180) { + rotated_event.axisrel = -rotated_event.axisrel; + } + else if (context->rotation == 270) { + rotated_event.axis = DIAI_Y; + rotated_event.axisrel = -rotated_event.axisrel; + } + } + else if (event->axis == DIAI_Y) { + if (context->rotation == 90) { + rotated_event.axis = DIAI_X; + rotated_event.axisrel = -rotated_event.axisrel; + } + else if (context->rotation == 180) { + rotated_event.axisrel = -rotated_event.axisrel; + } + else if (context->rotation == 270) { + rotated_event.axis = DIAI_X; + } + } + } + else if (event->flags & DIEF_AXISABS) { + rotated_event = *event; + event = &rotated_event; + + if (event->axis == DIAI_X) { + if (context->rotation == 90) { + rotated_event.axis = DIAI_Y; + } + else if (context->rotation == 180) { + rotated_event.axisabs = stack->rotated_width - rotated_event.axisabs; + } + else if (context->rotation == 270) { + rotated_event.axis = DIAI_Y; + rotated_event.axisabs = stack->rotated_width - rotated_event.axisabs; + } + } + else if (event->axis == DIAI_Y) { + if (context->rotation == 90) { + rotated_event.axis = DIAI_X; + rotated_event.axisabs = stack->rotated_height - rotated_event.axisabs; + } + else if (context->rotation == 180) { + rotated_event.axisabs = stack->rotated_height - rotated_event.axisabs; + } + else if (context->rotation == 270) { + rotated_event.axis = DIAI_X; + } + } + } + + if (event->flags & DIEF_AXISREL) { + int rel = event->axisrel; + + /* handle cursor acceleration */ + if (rel > stack->cursor.threshold) + rel += (rel - stack->cursor.threshold) + * stack->cursor.numerator + / stack->cursor.denominator; + else if (rel < -stack->cursor.threshold) + rel += (rel + stack->cursor.threshold) + * stack->cursor.numerator + / stack->cursor.denominator; + + switch (event->axis) { + case DIAI_X: + data->cursor_dx += rel; + break; + + case DIAI_Y: + data->cursor_dy += rel; + break; + + case DIAI_Z: + flush_motion( stack, data, wmdata ); + + handle_wheel( stack, data, - event->axisrel ); + break; + + default: + ; + } + } + else if (event->flags & DIEF_AXISABS) { + int axismin = 0; + int axisabs = event->axisabs; + + if (event->flags & DIEF_MIN) { + axismin = event->min; + + axisabs -= axismin; + } + + switch (event->axis) { + case DIAI_X: + if (event->flags & DIEF_MAX) + axisabs = axisabs * stack->width / (event->max - axismin + 1); + + data->cursor_dx = axisabs - stack->cursor.x; + break; + + case DIAI_Y: + if (event->flags & DIEF_MAX) + axisabs = axisabs * stack->height / (event->max - axismin + 1); + + data->cursor_dy = axisabs - stack->cursor.y; + break; + + default: + ; + } + } + + return DFB_OK; +} + +/**************************************************************************************************/ +/**************************************************************************************************/ + +static void +wm_get_info( CoreWMInfo *info ) +{ + info->version.major = 0; + info->version.minor = 3; + info->version.binary = 2; + + snprintf( info->name, DFB_CORE_WM_INFO_NAME_LENGTH, "Default" ); + snprintf( info->vendor, DFB_CORE_WM_INFO_VENDOR_LENGTH, "directfb.org" ); + + info->wm_data_size = sizeof(WMData); + info->stack_data_size = sizeof(StackData); + info->window_data_size = sizeof(WindowData); +} + +static DFBResult +wm_initialize( CoreDFB *core, void *wm_data, void *shared_data ) +{ + WMData *data = wm_data; + + data->core = core; + + return DFB_OK; +} + +static DFBResult +wm_join( CoreDFB *core, void *wm_data, void *shared_data ) +{ + WMData *data = wm_data; + + data->core = core; + + return DFB_OK; +} + +static DFBResult +wm_shutdown( bool emergency, void *wm_data, void *shared_data ) +{ + return DFB_OK; +} + +static DFBResult +wm_leave( bool emergency, void *wm_data, void *shared_data ) +{ + return DFB_OK; +} + +static DFBResult +wm_suspend( void *wm_data, void *shared_data ) +{ + return DFB_OK; +} + +static DFBResult +wm_resume( void *wm_data, void *shared_data ) +{ + return DFB_OK; +} + +static DFBResult +wm_post_init( void *wm_data, void *shared_data ) +{ + return DFB_OK; +} + +/**************************************************************************************************/ + +static DFBResult +wm_init_stack( CoreWindowStack *stack, + void *wm_data, + void *stack_data ) +{ + int i; + StackData *data = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + + data->stack = stack; + + /* Initialize update manager. */ + dfb_updates_init( &data->updates, data->update_regions, MAX_UPDATE_REGIONS ); + + fusion_vector_init( &data->windows, 64, stack->shmpool ); + + for (i=0; ikeys[i].code = -1; + + D_MAGIC_SET( data, StackData ); + + return DFB_OK; +} + +static DFBResult +wm_close_stack( CoreWindowStack *stack, + void *wm_data, + void *stack_data ) +{ + DirectLink *l, *next; + StackData *data = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + + D_MAGIC_ASSERT( data, StackData ); + D_MAGIC_CLEAR( data ); + + D_ASSUME( fusion_vector_is_empty( &data->windows ) ); + + if (fusion_vector_has_elements( &data->windows )) { + int i; + CoreWindow *window; + + fusion_vector_foreach (window, i, data->windows) { + D_WARN( "setting window->stack = NULL" ); + window->stack = NULL; + } + } + + fusion_vector_destroy( &data->windows ); + + /* Destroy backing store of software cursor. */ + if (data->cursor_bs) + dfb_surface_unlink( &data->cursor_bs ); + + /* Free grabbed keys. */ + direct_list_foreach_safe (l, next, data->grabbed_keys) + SHFREE( stack->shmpool, l ); + + return DFB_OK; +} + +static DFBResult +wm_set_active( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + bool active ) +{ + StackData *data = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + + D_MAGIC_ASSERT( data, StackData ); + + D_ASSUME( data->active != active ); + + if (data->active == active) + return DFB_OK; + + data->active = active; + + if (active) + return dfb_windowstack_repaint_all( stack ); + + /* Force release of all pressed keys. */ + return wm_flush_keys( stack, wm_data, stack_data ); +} + +static DFBResult +wm_resize_stack( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + int width, + int height ) +{ + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + + return DFB_OK; +} + +static DFBResult +wm_process_input( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + const DFBInputEvent *event ) +{ + DFBResult ret; + StackData *data = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + D_ASSERT( event != NULL ); + + D_MAGIC_ASSERT( data, StackData ); + + D_DEBUG_AT( WM_Default, "Processing input event (device %d, type 0x%08x, flags 0x%08x)...\n", + event->device_id, event->type, event->flags ); + + /* FIXME: handle multiple devices */ + if (event->flags & DIEF_BUTTONS) + data->buttons = event->buttons; + + if (event->flags & DIEF_MODIFIERS) + data->modifiers = event->modifiers; + + if (event->flags & DIEF_LOCKS) + data->locks = event->locks; + + if (event->type != DIET_AXISMOTION) + flush_motion( stack, data, wm_data ); + + switch (event->type) { + case DIET_KEYPRESS: + ret = handle_key_press( stack, data, wm_data, event ); + break; + + case DIET_KEYRELEASE: + ret = handle_key_release( stack, data, event ); + break; + + case DIET_BUTTONPRESS: + ret = handle_button_press( stack, data, event ); + break; + + case DIET_BUTTONRELEASE: + ret = handle_button_release( stack, data, event ); + break; + + case DIET_AXISMOTION: + ret = handle_axis_motion( stack, data, wm_data, event ); + break; + + default: + D_ONCE( "unknown input event type" ); + ret = DFB_UNSUPPORTED; + break; + } + + if (!D_FLAGS_IS_SET( event->flags, DIEF_FOLLOW )) + flush_motion( stack, data, wm_data ); + + process_updates( data, wm_data, stack, NULL, DSFLIP_NONE ); + + return ret; +} + +static DFBResult +wm_flush_keys( CoreWindowStack *stack, + void *wm_data, + void *stack_data ) +{ + int i; + StackData *data = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + + D_MAGIC_ASSERT( data, StackData ); + + for (i=0; ikeys[i].code != -1) { + DFBWindowEvent we; + + we.type = DWET_KEYUP; + we.key_code = data->keys[i].code; + we.key_id = data->keys[i].id; + we.key_symbol = data->keys[i].symbol; + + post_event( data->keys[i].owner, data, &we ); + + data->keys[i].code = -1; + } + } + + return DFB_OK; +} + +static DFBResult +wm_window_at( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + int x, + int y, + CoreWindow **ret_window ) +{ + StackData *data = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + D_ASSERT( ret_window != NULL ); + + *ret_window = window_at_pointer( stack, data, wm_data, x, y ); + + return DFB_OK; +} + +static DFBResult +wm_window_lookup( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + DFBWindowID window_id, + CoreWindow **ret_window ) +{ + int i; + CoreWindow *window = NULL; + StackData *data = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + D_ASSERT( ret_window != NULL ); + + D_MAGIC_ASSERT( data, StackData ); + + fusion_vector_foreach_reverse (window, i, data->windows) { + if (window->id == window_id) { + break; + } + } + + *ret_window = window; + return DFB_OK; +} + +static DFBResult +wm_enum_windows( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + CoreWMWindowCallback callback, + void *callback_ctx ) +{ + int i; + CoreWindow *window = NULL; + StackData *data = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + D_ASSERT( callback != NULL ); + + D_MAGIC_ASSERT( data, StackData ); + + fusion_vector_foreach_reverse (window, i, data->windows) { + if (callback( window, callback_ctx ) != DFENUM_OK) + break; + } + + return DFB_OK; +} + +/**************************************************************************************************/ + +static DFBResult +wm_get_insets( CoreWindowStack *stack, + CoreWindow *window, + DFBInsets *insets ) +{ + insets->l = 0; + insets->t = 0; + insets->r = 0; + insets->b = 0; + + return DFB_OK; +} + + +static DFBResult +wm_preconfigure_window( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + CoreWindow *window, + void *window_data ) +{ + if (window->config.association) + return DFB_UNIMPLEMENTED; + + return DFB_OK; +} + +static DFBResult +wm_set_window_property( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + CoreWindow *window, + void *window_data, + const char *key, + void *value, + void **ret_old_value ) +{ + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( key != NULL ); + + fusion_object_set_property((FusionObject*)window, + key,value,ret_old_value); + return DFB_OK; +} + +static DFBResult +wm_get_window_property( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + CoreWindow *window, + void *window_data, + const char *key, + void **ret_value ) +{ + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( key != NULL ); + D_ASSERT( ret_value != NULL ); + + *ret_value = fusion_object_get_property((FusionObject*)window,key); + return DFB_OK; +} + + +static DFBResult +wm_remove_window_property( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + CoreWindow *window, + void *window_data, + const char *key, + void **ret_value ) +{ + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( key != NULL ); + + fusion_object_remove_property((FusionObject*)window,key,ret_value); + return DFB_OK; +} + +static DFBResult +wm_add_window( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + CoreWindow *window, + void *window_data ) +{ + WindowData *data = window_data; + StackData *sdata = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + + D_MAGIC_ASSERT( sdata, StackData ); + + /* Initialize window data. */ + data->window = window; + data->stack_data = stack_data; + data->priority = get_priority( window ); + + if (window->region) + dfb_layer_region_get_configuration( window->region, &data->config ); + + D_MAGIC_SET( data, WindowData ); + + /* Actually add the window to the stack. */ + insert_window( stack, sdata, window, data ); + + /* Possibly switch focus to the new window. */ + update_focus( stack, sdata, wm_data ); + + process_updates( sdata, wm_data, stack, window->primary_region, DSFLIP_NONE ); + + return DFB_OK; +} + +static DFBResult +wm_remove_window( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + CoreWindow *window, + void *window_data ) +{ + WindowData *data = window_data; + StackData *sdata = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + D_ASSERT( window != NULL ); + D_ASSERT( window_data != NULL ); + + D_MAGIC_ASSERT( data, WindowData ); + D_MAGIC_ASSERT( sdata, StackData ); + + remove_window( stack, sdata, window, data ); + + /* Free key list. */ + if (window->config.keys) { + SHFREE( stack->shmpool, window->config.keys ); + + window->config.keys = NULL; + window->config.num_keys = 0; + } + + D_MAGIC_CLEAR( data ); + + return DFB_OK; +} + +static DFBResult +wm_set_window_config( CoreWindow *window, + void *wm_data, + void *window_data, + const CoreWindowConfig *config, + CoreWindowConfigFlags flags ) +{ + DFBResult ret; + CoreWindowStack *stack; + + D_ASSERT( window != NULL ); + D_ASSERT( window->stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( config != NULL ); + + stack = window->stack; + D_ASSERT( stack != NULL ); + + if (flags & CWCF_OPTIONS) { + if ((window->config.options & DWOP_SCALE) && !(config->options & DWOP_SCALE) && window->surface) { + if (window->config.bounds.w != window->surface->config.size.w || + window->config.bounds.h != window->surface->config.size.h) + { + ret = dfb_surface_reformat( window->surface, + window->config.bounds.w, + window->config.bounds.h, + window->surface->config.format ); + if (ret) { + D_DERROR( ret, "WM/Default: Could not resize surface " + "(%dx%d -> %dx%d) to remove DWOP_SCALE!\n", + window->surface->config.size.w, + window->surface->config.size.h, + window->config.bounds.w, + window->config.bounds.h ); + return ret; + } + } + } + + window->config.options = config->options; + } + + if (flags & CWCF_EVENTS) + window->config.events = config->events; + + if (flags & CWCF_COLOR) + return DFB_UNSUPPORTED; + + if (flags & CWCF_COLOR_KEY) + window->config.color_key = config->color_key; + + if (flags & CWCF_OPAQUE) + window->config.opaque = config->opaque; + + if (flags & CWCF_OPACITY && !config->opacity) + set_opacity( window, window_data, wm_data, config->opacity ); + + if (flags == (CWCF_POSITION | CWCF_SIZE)) { + ret = set_window_bounds (window, wm_data, window_data, + config->bounds.x, config->bounds.y, + config->bounds.w, config->bounds.h); + if (ret) + return ret; + } + else { + if (flags & CWCF_POSITION) { + ret = move_window( window, window_data, + config->bounds.x - window->config.bounds.x, + config->bounds.y - window->config.bounds.y ); + if (ret) + return ret; + } + + if (flags & CWCF_SIZE) { + ret = resize_window( window, wm_data, window_data, config->bounds.w, config->bounds.h ); + if (ret) + return ret; + } + } + + if (flags & CWCF_ROTATION) { + update_window( window, window_data, NULL, DSFLIP_NONE, false, false, false ); + + window->config.rotation = config->rotation; + + update_window( window, window_data, NULL, DSFLIP_NONE, false, false, false ); + } + + if (flags & CWCF_STACKING) + restack_window( window, window_data, window, window_data, 0, config->stacking ); + + if (flags & CWCF_OPACITY && config->opacity) + set_opacity( window, window_data, wm_data, config->opacity ); + + if (flags & CWCF_KEY_SELECTION) { + if (config->key_selection == DWKS_LIST) { + unsigned int bytes = sizeof(DFBInputDeviceKeySymbol) * config->num_keys; + DFBInputDeviceKeySymbol *keys; + + D_ASSERT( config->keys != NULL ); + D_ASSERT( config->num_keys > 0 ); + + keys = SHMALLOC( window->stack->shmpool, bytes ); + if (!keys) { + D_ERROR( "WM/Default: Could not allocate %d bytes for list " + "of selected keys (%d)!\n", bytes, config->num_keys ); + return D_OOSHM(); + } + + direct_memcpy( keys, config->keys, bytes ); + + qsort( keys, config->num_keys, sizeof(DFBInputDeviceKeySymbol), keys_compare ); + + if (window->config.keys) + SHFREE( window->stack->shmpool, window->config.keys ); + + window->config.keys = keys; + window->config.num_keys = config->num_keys; + } + else if (window->config.keys) { + SHFREE( window->stack->shmpool, window->config.keys ); + + window->config.keys = NULL; + window->config.num_keys = 0; + } + + window->config.key_selection = config->key_selection; + } + + process_updates( stack->stack_data, wm_data, stack, window->primary_region, DSFLIP_NONE ); + + return DFB_OK; +} + +static DFBResult +wm_restack_window( CoreWindow *window, + void *wm_data, + void *window_data, + CoreWindow *relative, + void *relative_data, + int relation ) +{ + DFBResult ret; + WindowData *data = window_data; + StackData *sdata; + + D_ASSERT( window != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( window_data != NULL ); + + D_MAGIC_ASSERT( data, WindowData ); + + D_ASSERT( relative == NULL || relative_data != NULL ); + + D_ASSERT( relative == NULL || relative == window || relation != 0); + + sdata = data->stack_data; + D_ASSERT( sdata != NULL ); + D_ASSERT( sdata->stack != NULL ); + + ret = restack_window( window, window_data, relative, + relative_data, relation, window->config.stacking ); + if (ret) + return ret; + + /* Possibly switch focus to window now under the cursor */ + update_focus( sdata->stack, sdata, wm_data ); + + process_updates( sdata, wm_data, window->stack, window->primary_region, DSFLIP_NONE ); + + return DFB_OK; +} + +static DFBResult +wm_grab( CoreWindow *window, + void *wm_data, + void *window_data, + CoreWMGrab *grab ) +{ + StackData *sdata; + WindowData *wdata = window_data; + + D_ASSERT( window != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( grab != NULL ); + D_ASSERT( wdata->stack_data != NULL ); + + sdata = wdata->stack_data; + + switch (grab->target) { + case CWMGT_KEYBOARD: + return grab_keyboard( window, window_data ); + + case CWMGT_POINTER: + return grab_pointer( window, window_data ); + + case CWMGT_KEY: + return grab_key( window, window_data, grab->symbol, grab->modifiers ); + + case CWMGT_UNSELECTED_KEYS: + if (sdata->unselkeys_window) + return DFB_LOCKED; + + sdata->unselkeys_window = window; + return DFB_OK; + + default: + D_BUG( "unknown grab target" ); + break; + } + + return DFB_BUG; +} + +static DFBResult +wm_ungrab( CoreWindow *window, + void *wm_data, + void *window_data, + CoreWMGrab *grab ) +{ + StackData *sdata; + WindowData *wdata = window_data; + + D_ASSERT( window != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( window_data != NULL ); + D_ASSERT( grab != NULL ); + D_ASSERT( wdata->stack_data != NULL ); + + sdata = wdata->stack_data; + + switch (grab->target) { + case CWMGT_KEYBOARD: + return ungrab_keyboard( window, window_data ); + + case CWMGT_POINTER: + return ungrab_pointer( window, window_data, wm_data ); + + case CWMGT_KEY: + return ungrab_key( window, window_data, grab->symbol, grab->modifiers ); + + case CWMGT_UNSELECTED_KEYS: + if (sdata->unselkeys_window == window) + sdata->unselkeys_window = NULL; + + return DFB_OK; + + default: + D_BUG( "unknown grab target" ); + break; + } + + return DFB_BUG; +} + +static DFBResult +wm_request_focus( CoreWindow *window, + void *wm_data, + void *window_data ) +{ + D_ASSERT( window != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( window_data != NULL ); + + return request_focus( window, window_data ); +} + +static DFBResult +wm_begin_updates( CoreWindow *window, + void *wm_data, + void *window_data, + const DFBRegion *update ) +{ + D_ASSERT( window != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( window_data != NULL ); + + return DFB_OK; +} + +/**************************************************************************************************/ + +static DFBResult +wm_update_stack( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + const DFBRegion *region, /* stack coordinates */ + DFBSurfaceFlipFlags flags ) +{ + StackData *data = stack_data; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + D_ASSERT( region != NULL ); + + D_MAGIC_ASSERT( data, StackData ); + + dfb_updates_add( &data->updates, region ); + + process_updates( data, wm_data, stack, NULL, flags ); + + return DFB_OK; +} + +static DFBResult +wm_update_window( CoreWindow *window, + void *wm_data, + void *window_data, + const DFBRegion *region, /* surface coordinates! */ + DFBSurfaceFlipFlags flags ) +{ + CoreWindowStack *stack; + + D_ASSERT( window != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( window_data != NULL ); + + DFB_REGION_ASSERT_IF( region ); + + stack = window->stack; + D_ASSERT( stack != NULL ); + + update_window( window, window_data, region, flags, false, false, true ); + + process_updates( stack->stack_data, wm_data, stack, window->primary_region, flags ); + + return DFB_OK; +} + +static DFBResult +wm_update_cursor( CoreWindowStack *stack, + void *wm_data, + void *stack_data, + CoreCursorUpdateFlags flags ) +{ + DFBResult ret; + DFBRegion old_dest; + DFBRegion old_region; + WMData *wmdata = wm_data; + StackData *data = stack_data; + bool restored = false; + CoreLayerContext *context; + CoreLayerRegion *primary; + CoreSurface *surface; + + D_ASSERT( stack != NULL ); + D_ASSERT( wm_data != NULL ); + D_ASSERT( stack_data != NULL ); + + D_MAGIC_ASSERT( data, StackData ); + + old_region = data->cursor_region; + + transform_stack_to_dest( stack, &old_region, &old_dest ); + + if (flags & (CCUF_ENABLE | CCUF_POSITION | CCUF_SIZE)) { + data->cursor_bs_valid = false; + + data->cursor_region.x1 = stack->cursor.x - stack->cursor.hot.x; + data->cursor_region.y1 = stack->cursor.y - stack->cursor.hot.y; + data->cursor_region.x2 = data->cursor_region.x1 + stack->cursor.size.w - 1; + data->cursor_region.y2 = data->cursor_region.y1 + stack->cursor.size.h - 1; + + if (!dfb_region_intersect( &data->cursor_region, 0, 0, stack->width - 1, stack->height - 1 )) { + D_BUG( "invalid cursor region" ); + return DFB_BUG; + } + } + + /* Optimize case of invisible cursor moving. */ + if (!(flags & ~(CCUF_POSITION | CCUF_SHAPE)) && (!stack->cursor.opacity || !stack->cursor.enabled)) + return DFB_OK; + + context = stack->context; + D_ASSERT( context != NULL ); + + if (!data->cursor_bs) { + CoreSurface *cursor_bs; + DFBSurfaceCapabilities caps = DSCAPS_NONE; + DFBDimension size = stack->cursor.size; + + D_ASSUME( flags & CCUF_ENABLE ); + + dfb_surface_caps_apply_policy( stack->cursor.policy, &caps ); + + if (stack->rotation == 90 || stack->rotation == 270) + D_UTIL_SWAP( size.w, size.h ); + + /* Create the cursor backing store surface. */ + ret = dfb_surface_create_simple( wmdata->core, size.w, size.h, + context->config.pixelformat, caps, CSTF_SHARED | CSTF_CURSOR, + 0, /* FIXME: no shared cursor objects, no cursor id */ + NULL, &cursor_bs ); + if (ret) { + D_ERROR( "WM/Default: Failed creating backing store for cursor!\n" ); + return ret; + } + + ret = dfb_surface_globalize( cursor_bs ); + D_ASSERT( ret == DFB_OK ); + + data->cursor_bs = cursor_bs; + } + + D_ASSERT( data->cursor_bs != NULL ); + + /* Get the primary region. */ + ret = dfb_layer_context_get_primary_region( context, false, &primary ); + if (ret) + return ret; + + surface = primary->surface; + D_MAGIC_ASSERT( surface, CoreSurface ); + + if (flags & CCUF_ENABLE) { + /* Ensure valid back buffer. From now on swapping is prevented until cursor is disabled. + * FIXME: Keep a flag to know when back/front have been swapped and need a sync. + */ + switch (context->config.buffermode) { + case DLBM_BACKVIDEO: + case DLBM_TRIPLE: + dfb_gfx_copy( surface, surface, NULL ); + break; + + default: + break; + } + } + + /* restore region under cursor */ + if (data->cursor_drawn && dfb_region_intersect( &old_dest, 0, 0, + surface->config.size.w - 1, surface->config.size.h - 1 )) + { + DFBRectangle rect = { 0, 0, + old_dest.x2 - old_dest.x1 + 1, + old_dest.y2 - old_dest.y1 + 1 }; + + D_ASSERT( stack->cursor.opacity || (flags & CCUF_OPACITY) ); + + dfb_gfx_copy_to( data->cursor_bs, surface, &rect, old_dest.x1, old_dest.y1, false ); + + data->cursor_drawn = false; + + restored = true; + } + + if (flags & CCUF_SIZE) { + DFBDimension size = stack->cursor.size; + + if (stack->rotation == 90 || stack->rotation == 270) + D_UTIL_SWAP( size.w, size.h ); + + ret = dfb_surface_reformat( data->cursor_bs, size.w, size.h, data->cursor_bs->config.format ); + if (ret) + D_DERROR( ret, "WM/Default: Failed resizing backing store for cursor from %dx%d to %dx%d!\n", + data->cursor_bs->config.size.w, data->cursor_bs->config.size.h, + stack->cursor.size.w, stack->cursor.size.h ); + } + + if (flags & CCUF_DISABLE) { + dfb_surface_unlink( &data->cursor_bs ); + } + else if (stack->cursor.opacity) { + DFBRegion dest; + CoreLayer *layer = dfb_layer_at( context->layer_id ); + CardState *state = &layer->state; + + transform_stack_to_dest( stack, &data->cursor_region, &dest ); + + if (!dfb_region_intersect( &dest, 0, 0, surface->config.size.w - 1, surface->config.size.h - 1 )) { + if (restored) + dfb_layer_region_flip_update( primary, &old_dest, DSFLIP_BLIT ); + dfb_layer_region_unref( primary ); + return DFB_OK; + } + + /* backup region under cursor */ + if (!data->cursor_bs_valid) { + DFBRectangle rect = DFB_RECTANGLE_INIT_FROM_REGION( &dest ); + + D_ASSERT( !data->cursor_drawn ); + + /* FIXME: this requires using blitted flipping all the time, + but fixing it seems impossible, for now DSFLIP_BLIT is forced + in repaint_stack() when the cursor is enabled. */ + dfb_gfx_copy_to( surface, data->cursor_bs, &rect, 0, 0, true ); + + data->cursor_bs_valid = true; + } + + /* Set destination. */ + state->destination = surface; + state->modified |= SMF_DESTINATION; + + /* Set clipping region. */ + dfb_state_set_clip( state, &dest ); + + /* draw cursor */ + draw_cursor( stack, data, state, &data->cursor_region ); + + /* Reset destination. */ + state->destination = NULL; + state->modified |= SMF_DESTINATION; + + data->cursor_drawn = true; + + if (restored) { + if (dfb_region_region_intersects( &old_dest, &dest )) + dfb_region_region_union( &old_dest, &dest ); + else + dfb_layer_region_flip_update( primary, &dest, DSFLIP_BLIT ); + + dfb_layer_region_flip_update( primary, &old_dest, DSFLIP_BLIT ); + } + else + dfb_layer_region_flip_update( primary, &dest, DSFLIP_BLIT ); + + /* Pan to follow the cursor? */ + if (primary->config.source.w < surface->config.size.w || primary->config.source.h < surface->config.size.h) { + DFBRectangle source = primary->config.source; + + if (stack->rotation) + D_UNIMPLEMENTED(); + + if (source.x > stack->cursor.x) + source.x = stack->cursor.x; + else if (source.x + source.w - 1 < stack->cursor.x) + source.x = stack->cursor.x - source.w + 1; + + if (source.y > stack->cursor.y) + source.y = stack->cursor.y; + else if (source.y + source.h - 1 < stack->cursor.y) + source.y = stack->cursor.y - source.h + 1; + + dfb_layer_context_set_sourcerectangle( context, &source ); + } + } + else if (restored) + dfb_layer_region_flip_update( primary, &old_dest, DSFLIP_BLIT ); + + /* Unref primary region. */ + dfb_layer_region_unref( primary ); + + return DFB_OK; +} + -- cgit