diff options
author | Jon TURNEY <jon.turney@dronecode.org.uk> | 2013-07-03 00:07:09 +0100 |
---|---|---|
committer | Jon TURNEY <jon.turney@dronecode.org.uk> | 2013-07-26 18:00:28 +0100 |
commit | c3ef6a82eb299b9ba9b8f52215e7d1d74da71b9e (patch) | |
tree | 514a29e5eb052d2dea5bc1851b11a286c1872e71 | |
parent | 0f1787e4925e2545ef7e48bde10908e66a68ef0b (diff) |
Implement iconic state
Implement the iconic state
- Don't directly create/destroy the native window on map/unmap, track the window
state and create/destroy window as needed
- Add _xcwm_window_change_state() function to contain this state change logic
and do all the changes need to apply a state change
- re-write xcwm_window_iconify() and xcwm_window_deiconify() so they just cause
the right message to be sent to the WM thread to apply the state change required
- Check WM_HINTS.initial_state on MAP to determine initial state
- Respond to WM_CHANGE_STATE client message to change state to iconic
- Revise _xcwm_windows_adopt() to determine intital window state correctly
- Add _xcwm_atoms_get_wm_state() function to discover current window state
- Add XCWM_EVENT_WINDOW_STATE event to signal when window state changes
- Don't use XCWM_WINDOW_STATE_UNKNOWN anywhere
-rw-r--r-- | include/xcwm/event.h | 1 | ||||
-rw-r--r-- | include/xcwm/window.h | 14 | ||||
-rw-r--r-- | src/libxcwm/atoms.c | 58 | ||||
-rw-r--r-- | src/libxcwm/event_loop.c | 210 | ||||
-rw-r--r-- | src/libxcwm/window.c | 32 | ||||
-rw-r--r-- | src/libxcwm/xcwm_internal.h | 9 |
6 files changed, 246 insertions, 78 deletions
diff --git a/include/xcwm/event.h b/include/xcwm/event.h index 504fe9c..3647ab0 100644 --- a/include/xcwm/event.h +++ b/include/xcwm/event.h @@ -45,6 +45,7 @@ typedef enum xcwm_event_type_t { XCWM_EVENT_WINDOW_APPEARANCE, XCWM_EVENT_WINDOW_SHAPE, XCWM_EVENT_WINDOW_CONFIGURE, + XCWM_EVENT_WINDOW_STATE, XCWM_EVENT_CURSOR, XCWM_EVENT_EXIT, } xcwm_event_type_t; diff --git a/include/xcwm/window.h b/include/xcwm/window.h index bd61944..77cd6b2 100644 --- a/include/xcwm/window.h +++ b/include/xcwm/window.h @@ -67,9 +67,10 @@ typedef enum xcwm_window_type_t xcwm_window_type_t; * Enumeration for possible supported window states. */ enum xcwm_window_state_t { - XCWM_WINDOW_STATE_UNKNOWN = 0, - XCWM_WINDOW_STATE_NORMAL, - XCWM_WINDOW_STATE_ICONIC + XCWM_WINDOW_STATE_UNKNOWN = -1, + XCWM_WINDOW_STATE_NORMAL = 1, + XCWM_WINDOW_STATE_ICONIC = 3, + XCWM_WINDOW_STATE_WITHDRAWN = 0 }; typedef enum xcwm_window_state_t xcwm_window_state_t; @@ -246,6 +247,13 @@ void xcwm_window_deiconify(xcwm_window_t *window); /** + * Get the window state. + * @param window The window + * @return The window state + */ +xcwm_window_state_t xcwm_window_get_state(xcwm_window_t *window); + +/** * Get the opacity for the window. * @param window The window to get opacity data for. * @return The window opacity, in the range 0 (transparent) to 0xFFFFFFFF (opaque) diff --git a/src/libxcwm/atoms.c b/src/libxcwm/atoms.c index 8596d95..2cfd79c 100644 --- a/src/libxcwm/atoms.c +++ b/src/libxcwm/atoms.c @@ -196,6 +196,9 @@ _xcwm_atoms_init(xcwm_context_t *context) /* WM_STATE atom */ context->atoms.wm_state_atom = _xcwm_atom_get(context, "WM_STATE"); + /* WM_CHANGE_STATE atom */ + context->atoms.wm_change_state_atom = _xcwm_atom_get(context, "WM_CHANGE_STATE"); + return 0; } @@ -475,6 +478,9 @@ set_window_opacity(xcwm_window_t *window, xcwm_property_t *property) void _xcwm_atoms_set_wm_state(xcwm_window_t *window, xcwm_window_state_t state) { + printf("_xcwm_atoms_set_wm_state: XID 0x%08x state %d\n", + window->window_id, state); + /* xcb_icccm_wm_state_t icccm_state; */ uint32_t icccm_state[2]; @@ -488,7 +494,6 @@ _xcwm_atoms_set_wm_state(xcwm_window_t *window, xcwm_window_state_t state) icccm_state[1] = XCB_NONE; break; } - case XCWM_WINDOW_STATE_ICONIC: { ewmh_atom_cnt = 1; @@ -499,6 +504,12 @@ _xcwm_atoms_set_wm_state(xcwm_window_t *window, xcwm_window_state_t state) ewmh_state[0] = window->context->atoms.ewmh_conn._NET_WM_STATE_HIDDEN; break; } + case XCWM_WINDOW_STATE_WITHDRAWN: + { + icccm_state[0] = XCB_ICCCM_WM_STATE_WITHDRAWN; + icccm_state[1] = XCB_NONE; + break; + } default: { /* No need to attempt to update the state */ @@ -530,6 +541,51 @@ _xcwm_atoms_set_wm_state(xcwm_window_t *window, xcwm_window_state_t state) } } +xcwm_window_state_t +_xcwm_atoms_get_wm_state(xcwm_window_t *window) +{ + xcwm_window_state_t state = XCWM_WINDOW_STATE_WITHDRAWN; + xcb_get_property_cookie_t cookie; + + cookie = xcb_get_property(window->context->conn, + 0, + window->window_id, + window->context->atoms.wm_state_atom, + window->context->atoms.wm_state_atom, + 0L, + 1L); + + xcb_get_property_reply_t *reply = xcb_get_property_reply(window->context->conn, cookie, NULL); + if (reply) { + int length = xcb_get_property_value_length(reply); + uint32_t *value = xcb_get_property_value(reply); + + if (value && length == 4) { + switch (*value) { + case XCB_ICCCM_WM_STATE_NORMAL: + state = XCWM_WINDOW_STATE_NORMAL; + break; + + case XCB_ICCCM_WM_STATE_ICONIC: + state = XCWM_WINDOW_STATE_ICONIC; + break; + + default: + printf("window XID %08x has an unknown WM_STATE value %d\n", window->window_id, *value); + /* fall through */ + + case XCB_ICCCM_WM_STATE_WITHDRAWN: + state = XCWM_WINDOW_STATE_WITHDRAWN; + break; + } + } + + free(reply); + } + + return state; +} + void _xcwm_atoms_release(xcwm_context_t *context) { diff --git a/src/libxcwm/event_loop.c b/src/libxcwm/event_loop.c index 572277e..ae5c375 100644 --- a/src/libxcwm/event_loop.c +++ b/src/libxcwm/event_loop.c @@ -168,6 +168,43 @@ _xcwm_window_composite_pixmap_update(xcwm_window_t *window) } } +static void +_xcwm_window_change_state(xcwm_window_t *window, xcwm_window_state_t newstate, xcwm_event_cb_t callback_ptr) +{ + xcwm_event_t return_evt; + + if (window->state == newstate) + return; + + xcwm_window_state_t oldstate = window->state; + window->state = newstate; + _xcwm_atoms_set_wm_state(window, newstate); + + if (newstate == XCWM_WINDOW_STATE_NORMAL) + xcb_map_window(window->context->conn, window->window_id); + else + xcb_unmap_window(window->context->conn, window->window_id); + + if (oldstate == XCWM_WINDOW_STATE_WITHDRAWN) { + /* transitioned out of withdrawn, create the naive window */ + return_evt.window = window; + return_evt.event_type = XCWM_EVENT_WINDOW_CREATE; + callback_ptr(&return_evt); + } + else if (newstate == XCWM_WINDOW_STATE_WITHDRAWN) { + /* transitioned into withdrawn, destroy the native window */ + return_evt.event_type = XCWM_EVENT_WINDOW_DESTROY; + return_evt.window = window; + callback_ptr(&return_evt); + } + else { + /* iconify/deiconifiy, change state */ + return_evt.event_type = XCWM_EVENT_WINDOW_STATE; + return_evt.window = window; + callback_ptr(&return_evt); + } +} + /* Generate a XCWM_EVENT_WINDOW_CREATE event for all existing mapped top-level windows when we start @@ -194,29 +231,37 @@ _xcwm_windows_adopt(xcwm_context_t *context, xcwm_event_cb_t callback_ptr) continue; } - if (attr->map_state == XCB_MAP_STATE_VIEWABLE) { - printf("window 0x%08x viewable\n", children[i]); + printf("window 0x%08x is %s\n", children[i], (attr->map_state == XCB_MAP_STATE_VIEWABLE) ? "mapped" : "withdrawn"); - window->mapped = 1; + xcwm_window_t *window = _xcwm_window_create(context, children[i], context->root_window->window_id); + if (!window) { + continue; + } - xcwm_window_t *window = _xcwm_window_create(context, children[i], context->root_window->window_id); - if (!window) { - continue; - } + if (attr->map_state == XCB_MAP_STATE_VIEWABLE) { + window->mapped = 1; _xcwm_window_composite_pixmap_update(window); - xcwm_event_t return_evt; - return_evt.window = window; - return_evt.event_type = XCWM_EVENT_WINDOW_CREATE; - - callback_ptr(&return_evt); - + /* state of a mapped window must be normal */ + _xcwm_window_change_state(window, XCWM_WINDOW_STATE_NORMAL, callback_ptr); } else { - printf("window 0x%08x non-viewable\n", children[i]); - window->mapped = 0; + + /* + state of an unmapped window must be either iconic or withdrawn + (careful: if the client didn't withdraw the window itself, we + must be sure to make it iconic, or the window will be lost...) + */ + /* + XXX: saveset should map any iconic windows on WM exit, but... + */ + xcwm_window_state_t state = _xcwm_atoms_get_wm_state(window); + if (state != XCWM_WINDOW_STATE_WITHDRAWN) + state = XCWM_WINDOW_STATE_ICONIC; + + _xcwm_window_change_state(window, state, callback_ptr); } free(attr); @@ -408,13 +453,20 @@ run_event_loop(void *thread_arg_struct) xcb_create_notify_event_t *notify = (xcb_create_notify_event_t *)evt; - /* We don't actually allow our client to create its - * window here, wait until the XCB_MAP_REQUEST */ - if (EVENT_DEBUG) { printf("CREATE_NOTIFY: XID 0x%08x\n", notify->window); } + xcwm_window_t *window = + _xcwm_get_window_node_by_window_id(notify->window); + if (window) { + printf("Window XID %08x already exists when created?\n", notify->window); + break; + } + + window = _xcwm_window_create(context, notify->window, + notify->parent); + break; } @@ -436,11 +488,6 @@ run_event_loop(void *thread_arg_struct) break; } - return_evt.event_type = XCWM_EVENT_WINDOW_DESTROY; - return_evt.window = window; - - callback_ptr(&return_evt); - _xcwm_window_composite_pixmap_release(window); // Release memory for the window @@ -457,37 +504,18 @@ run_event_loop(void *thread_arg_struct) printf("MAP_NOTIFY: XID 0x%08x\n", notify->window); } - /* notify->event holds parent of the window */ - xcwm_window_t *window = _xcwm_get_window_node_by_window_id(notify->window); if (!window) - { - /* - No MAP_REQUEST for override-redirect windows, so - need to create the xcwm_window_t for it now - */ - /* printf("MAP_NOTIFY without MAP_REQUEST\n"); */ - window = - _xcwm_window_create(context, notify->window, - notify->event); - - if (window) - { - window->mapped = 1; - _xcwm_window_composite_pixmap_update(window); - - return_evt.window = window; - return_evt.event_type = XCWM_EVENT_WINDOW_CREATE; - callback_ptr(&return_evt); - } - } - else - { - window->mapped = 1; - _xcwm_window_composite_pixmap_update(window); - } + break; + + /* state of a mapped window must be normal */ + _xcwm_window_change_state(window, XCWM_WINDOW_STATE_NORMAL, callback_ptr); + + window->mapped = 1; + + _xcwm_window_composite_pixmap_update(window); break; } @@ -501,19 +529,27 @@ run_event_loop(void *thread_arg_struct) printf("MAP_REQEUST: XID 0x%08x\n", request->window); } - /* Map the window */ - xcb_map_window(context->conn, request->window); - xcb_flush(context->conn); + xcwm_window_t *window = + _xcwm_get_window_node_by_window_id(request->window); - return_evt.window = - _xcwm_window_create(context, request->window, - request->parent); - if (!return_evt.window) { - break; + /* + If state is withdrawn, we should take note of if WM_HINTS.initial_state, + requests iconic state, otherwise set the state of the window to normal + */ + xcwm_window_state_t state = XCWM_WINDOW_STATE_NORMAL; + if (window->state == XCWM_WINDOW_STATE_WITHDRAWN) { + xcb_icccm_wm_hints_t hints; + xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_hints(event_conn, window->window_id); + if (xcb_icccm_get_wm_hints_reply(event_conn, cookie, &hints, NULL)) { + if (hints.initial_state == XCB_ICCCM_WM_STATE_ICONIC) + state = XCWM_WINDOW_STATE_ICONIC; + } } - return_evt.event_type = XCWM_EVENT_WINDOW_CREATE; - callback_ptr(&return_evt); + _xcwm_window_change_state(window, state, callback_ptr); + + xcb_flush(context->conn); + break; } @@ -527,24 +563,33 @@ run_event_loop(void *thread_arg_struct) } xcwm_window_t *window = - _xcwm_window_remove(event_conn, notify->window); + _xcwm_get_window_node_by_window_id(notify->window); if (!window) { - /* Not a window in the list, don't try and destroy */ + /* Not a window in the list, don't try and unmap */ break; } window->mapped = 0; - return_evt.event_type = XCWM_EVENT_WINDOW_DESTROY; - return_evt.window = window; + /* + Implementing ICCCM 4.1.4 is hard. - callback_ptr(&return_evt); + It's not clear how we tell precisely if this UNMAP_NOTIFY comes from the WM or the + client. - _xcwm_window_composite_pixmap_release(window); + If this looks like the client calling XWithdrawWindow() (i.e it's a synthetic event) + withdraw the window. + + If this looks like an UNMAP_NOTIFY we generated by us iconifying the window (i.e. we + are in the iconic state), then do nothing. + + */ + if ((notify->response_type & 0x80) || (window->state != XCWM_WINDOW_STATE_ICONIC)) { + /* Set the WM_STATE of the window to withdrawn */ + _xcwm_window_change_state(window, XCWM_WINDOW_STATE_WITHDRAWN, callback_ptr); + } - // Release memory for the window - _xcwm_window_release(window); break; } @@ -686,6 +731,35 @@ run_event_loop(void *thread_arg_struct) case XCB_MAPPING_NOTIFY: break; + case XCB_CLIENT_MESSAGE: + { + xcb_client_message_event_t *msg = + (xcb_client_message_event_t *)evt; + + if (EVENT_DEBUG) { + char *name = _xcwm_get_atom_name(event_conn, msg->type); + printf("CLIENT_MESSAGE: XID 0x%08x, type atom '%s' (%d)\n", + msg->window, name, msg->type); + free(name); + } + + // handle WM_CHANGE_STATE normal->iconic + if (msg->type == context->atoms.wm_change_state_atom) + { + xcwm_window_t *window = + _xcwm_get_window_node_by_window_id(msg->window); + + if (!window) + break; + + if (msg->data.data32[0] == XCB_ICCCM_WM_STATE_ICONIC) + _xcwm_window_change_state(window, XCWM_WINDOW_STATE_ICONIC, callback_ptr); + else + printf("CLIENT_MESSAGE: WM_CHANGE_STATE unexpected state %d/n", msg->data.data32[0]); + } + } + break; + default: { if (EVENT_DEBUG) { diff --git a/src/libxcwm/window.c b/src/libxcwm/window.c index 8424197..d18561d 100644 --- a/src/libxcwm/window.c +++ b/src/libxcwm/window.c @@ -126,6 +126,7 @@ _xcwm_window_create(xcwm_context_t *context, xcb_window_t new_window, window->local_data = 0; window->shape = 0; window->mapped = 0; + window->state = XCWM_WINDOW_STATE_WITHDRAWN; /* Find and set the parent */ window->parent = _xcwm_get_window_node_by_window_id(parent); @@ -159,9 +160,6 @@ _xcwm_window_create(xcwm_context_t *context, xcb_window_t new_window, /* add window to window list for this context */ window = _xcwm_add_window(window); - /* Set the WM_STATE of the window to normal */ - _xcwm_atoms_set_wm_state(window, XCWM_WINDOW_STATE_NORMAL); - return window; } @@ -537,19 +535,41 @@ xcwm_window_constrain_sizing(xcwm_window_t const *window, int *widthp, int *heig } #undef makemult - void xcwm_window_iconify(xcwm_window_t *window) { - _xcwm_atoms_set_wm_state(window, XCWM_WINDOW_STATE_ICONIC); + if (window->state != XCWM_WINDOW_STATE_ICONIC) { + xcb_client_message_event_t *msg = malloc(32); + + msg->type = XCB_CLIENT_MESSAGE; + msg->window = window->window_id; + msg->type = window->context->atoms.wm_change_state_atom; + msg->format = 32; + msg->data.data32[0] = XCB_ICCCM_WM_STATE_ICONIC; + + xcb_send_event(window->context->conn, 0, window->window_id, 0, (char *)msg); + xcb_flush(window->context->conn); + + free(msg); + } } void xcwm_window_deiconify(xcwm_window_t *window) { - _xcwm_atoms_set_wm_state(window, XCWM_WINDOW_STATE_NORMAL); + if (window->state != XCWM_WINDOW_STATE_NORMAL) { + xcb_map_window(window->context->conn, window->window_id); + } +} + +xcwm_window_state_t +xcwm_window_get_state(xcwm_window_t *window) +{ + return window->state; } + + /* Resize the window on server side */ void _xcwm_resize_window(xcb_connection_t *conn, xcb_window_t window, diff --git a/src/libxcwm/xcwm_internal.h b/src/libxcwm/xcwm_internal.h index a892eca..58c1918 100644 --- a/src/libxcwm/xcwm_internal.h +++ b/src/libxcwm/xcwm_internal.h @@ -53,6 +53,7 @@ struct xcwm_wm_atoms_t { xcb_atom_t wm_delete_window_atom; xcb_atom_t wm_state_atom; xcb_atom_t net_wm_window_type_splashscreen; + xcb_atom_t wm_change_state_atom; xcb_ewmh_connection_t ewmh_conn; }; typedef struct xcwm_wm_atoms_t xcwm_wm_atoms_t; @@ -98,6 +99,7 @@ struct xcwm_window_t { xcb_shm_segment_info_t shminfo; xcb_shape_get_rectangles_reply_t *shape; int mapped; + xcwm_window_state_t state; }; /** @@ -362,6 +364,13 @@ void _xcwm_atoms_set_wm_state(xcwm_window_t *window, xcwm_window_state_t state); /** + * Get the ICCCM WM_STATE for the given window + * @param window The window to get the state from + * @return state The current state + */ +xcwm_window_state_t _xcwm_atoms_get_wm_state(xcwm_window_t *window); + +/** * Note change of a property atom, and look up corresponding event type * @param window The property atom which has changed * @param event The corresponding event type |