summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon TURNEY <jon.turney@dronecode.org.uk>2013-07-03 00:07:09 +0100
committerJon TURNEY <jon.turney@dronecode.org.uk>2013-07-26 18:00:28 +0100
commitc3ef6a82eb299b9ba9b8f52215e7d1d74da71b9e (patch)
tree514a29e5eb052d2dea5bc1851b11a286c1872e71
parent0f1787e4925e2545ef7e48bde10908e66a68ef0b (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.h1
-rw-r--r--include/xcwm/window.h14
-rw-r--r--src/libxcwm/atoms.c58
-rw-r--r--src/libxcwm/event_loop.c210
-rw-r--r--src/libxcwm/window.c32
-rw-r--r--src/libxcwm/xcwm_internal.h9
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