#include "grab.h"
#include "prompt.h"
#include "focus.h"
+#include "focus_cycle.h"
#include "stacking.h"
#include "openbox.h"
#include "group.h"
#include "menuframe.h"
#include "keyboard.h"
#include "mouse.h"
-#include "render/render.h"
+#include "obrender/render.h"
#include "gettext.h"
#include "obt/display.h"
+#include "obt/xqueue.h"
#include "obt/prop.h"
#ifdef HAVE_UNISTD_H
gpointer data;
} ClientCallback;
-GList *client_list = NULL;
+GList *client_list = NULL;
static GSList *client_destroy_notifies = NULL;
static RrImage *client_default_icon = NULL;
static void client_get_all(ObClient *self, gboolean real);
static void client_get_startup_id(ObClient *self);
static void client_get_session_ids(ObClient *self);
+static void client_save_app_rule_values(ObClient *self);
static void client_get_area(ObClient *self);
static void client_get_desktop(ObClient *self);
static void client_get_state(ObClient *self);
static void client_get_shaped(ObClient *self);
-static void client_get_mwm_hints(ObClient *self);
static void client_get_colormap(ObClient *self);
static void client_set_desktop_recursive(ObClient *self,
guint target,
static void client_call_notifies(ObClient *self, GSList *list);
static void client_ping_event(ObClient *self, gboolean dead);
static void client_prompt_kill(ObClient *self);
-
+static gboolean client_can_steal_focus(ObClient *self,
+ gboolean allow_other_desktop,
+ Time steal_time, Time launch_time);
void client_startup(gboolean reconfig)
{
void client_add_destroy_notify(ObClientCallback func, gpointer data)
{
- ClientCallback *d = g_new(ClientCallback, 1);
+ ClientCallback *d = g_slice_new(ClientCallback);
d->func = func;
d->data = data;
client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
ClientCallback *d = it->data;
if (d->func == func) {
- g_free(d);
+ g_slice_free(ClientCallback, d);
client_destroy_notifies =
g_slist_delete_link(client_destroy_notifies, it);
break;
gboolean activate = FALSE;
ObAppSettings *settings;
gboolean transient = FALSE;
- Rect place, *monitor;
- Time launch_time, map_time;
+ Rect place;
+ Time launch_time;
+ guint32 user_time;
+ gboolean obplaced;
ob_debug("Managing window: 0x%lx", window);
- map_time = event_get_server_time();
-
/* choose the events we want to receive on the CLIENT window
(ObPrompt windows can request events too) */
attrib_set.event_mask = CLIENT_EVENTMASK |
/* create the ObClient struct, and populate it from the hints on the
window */
- self = g_new0(ObClient, 1);
+ self = g_slice_new0(ObClient);
self->obwin.type = OB_WINDOW_CLASS_CLIENT;
self->window = window;
self->prompt = prompt;
+ self->managed = TRUE;
/* non-zero defaults */
self->wmstate = WithdrawnState; /* make sure it gets updated first time */
ob_debug("Window type: %d", self->type);
ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
+ ob_debug("Window name: %s class: %s role: %s title: %s",
+ self->name, self->class, self->role, self->title);
- /* now we have all of the window's information so we can set this up.
- do this before creating the frame, so it can tell that we are still
- mapping and doesn't go applying things right away */
- client_setup_decor_and_functions(self, FALSE);
+ /* per-app settings override stuff from client_get_all, and return the
+ settings for other uses too. the returned settings is a shallow copy,
+ that needs to be freed with g_free(). */
+ settings = client_get_settings_state(self);
/* specify that if we exit, the window should not be destroyed and
should be reparented back to root automatically, unless we are managing
time now */
grab_server(FALSE);
- /* per-app settings override stuff from client_get_all, and return the
- settings for other uses too. the returned settings is a shallow copy,
- that needs to be freed with g_free(). */
- settings = client_get_settings_state(self);
/* the session should get the last say though */
client_restore_session_state(self);
/* tell startup notification that this app started */
launch_time = sn_app_started(self->startup_id, self->class, self->name);
+ if (!OBT_PROP_GET32(self->window, NET_WM_USER_TIME, CARDINAL, &user_time))
+ user_time = event_time();
+
/* do this after we have a frame.. it uses the frame to help determine the
WM_STATE to apply. */
client_change_state(self);
if (ob_state() != OB_STATE_STARTING &&
(!self->session || self->session->focused) &&
/* this means focus=true for window is same as config_focus_new=true */
- ((config_focus_new || (settings && settings->focus == 1)) ||
+ ((config_focus_new || settings->focus == 1) ||
client_search_focus_tree_full(self)) &&
+ /* NET_WM_USER_TIME 0 when mapping means don't focus */
+ (user_time != 0) &&
/* this checks for focus=false for the window */
- (!settings || settings->focus != 0) &&
- focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
+ settings->focus != 0 &&
+ focus_valid_target(self, self->desktop,
+ FALSE, FALSE, TRUE, TRUE, FALSE, FALSE,
+ settings->focus == 1))
{
activate = TRUE;
}
/* where the frame was placed is where the window was originally */
place = self->area;
- monitor = screen_physical_area_monitor(screen_find_monitor(&place));
/* figure out placement for the window if the window is new */
if (ob_state() == OB_STATE_RUNNING) {
"program + user specified" :
"BADNESS !?")))), place.width, place.height);
- /* splash screens are also returned as TRUE for transient,
- and so will be forced on screen below */
- transient = place_client(self, &place.x, &place.y, settings);
+ obplaced = place_client(self, &place.x, &place.y, settings);
+
+ /* watch for buggy apps that ask to be placed at (0,0) when there is
+ a strut there */
+ if (!obplaced && place.x == 0 && place.y == 0 &&
+ /* non-normal windows are allowed */
+ client_normal(self) &&
+ /* oldschool fullscreen windows are allowed */
+ !client_is_oldfullscreen(self, &place))
+ {
+ Rect *r;
+
+ r = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS, NULL);
+ if (r->x || r->y) {
+ place.x = r->x;
+ place.y = r->y;
+ ob_debug("Moving buggy app from (0,0) to (%d,%d)", r->x, r->y);
+ }
+ g_slice_free(Rect, r);
+ }
/* make sure the window is visible. */
client_find_onscreen(self, &place.x, &place.y,
it is up to the placement routines to avoid
the xinerama divides)
- splash screens get "transient" set to TRUE by
- the place_client call
+ children and splash screens are forced on
+ screen, but i don't remember why i decided to
+ do that.
*/
ob_state() == OB_STATE_RUNNING &&
- (transient ||
+ (self->type == OB_CLIENT_TYPE_DIALOG ||
+ self->type == OB_CLIENT_TYPE_SPLASH ||
(!((self->positioned & USPosition) ||
- (settings && settings->pos_given)) &&
+ settings->pos_given) &&
client_normal(self) &&
!self->session &&
/* don't move oldschool fullscreen windows to
fit inside the struts (fixes Acroread, which
makes its fullscreen window fit the screen
but it is not USSize'd or USPosition'd) */
- !(self->decorations == 0 &&
- RECT_EQUAL(place, *monitor)))));
+ !client_is_oldfullscreen(self, &place))));
}
/* if the window isn't user-sized, then make it fit inside
/* don't shrink oldschool fullscreen windows to fit inside the
struts (fixes Acroread, which makes its fullscreen window
fit the screen but it is not USSize'd or USPosition'd) */
- !(self->decorations == 0 && RECT_EQUAL(place, *monitor)))))
+ !client_is_oldfullscreen(self, &place))))
{
Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
place.width -= self->frame->size.left + self->frame->size.right;
place.height -= self->frame->size.top + self->frame->size.bottom;
- g_free(a);
+ g_slice_free(Rect, a);
}
ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
client_apply_startup_state(self, place.x, place.y,
place.width, place.height);
- g_free(monitor);
- monitor = NULL;
-
ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
activate ? "yes" : "no");
if (activate) {
- gboolean raise = FALSE;
-
- /* This is focus stealing prevention */
- ob_debug_type(OB_DEBUG_FOCUS,
- "Want to focus new window 0x%x at time %u "
- "launched at %u (last user interaction time %u)",
- self->window, map_time, launch_time,
- event_last_user_time);
-
- if (menu_frame_visible || moveresize_in_progress) {
- activate = FALSE;
- raise = TRUE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because the user is inside "
- "an Openbox menu or is move/resizing a window and "
- "we don't want to interrupt them");
- }
-
- /* if it's on another desktop */
- else if (!(self->desktop == screen_desktop ||
- self->desktop == DESKTOP_ALL) &&
- /* the timestamp is from before you changed desktops */
- launch_time && screen_desktop_user_time &&
- !event_time_after(launch_time, screen_desktop_user_time))
- {
- activate = FALSE;
- raise = TRUE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because its on another "
- "desktop");
- }
- /* If something is focused, and it's not our relative... */
- else if (focus_client && client_search_focus_tree_full(self) == NULL &&
- client_search_focus_group_full(self) == NULL)
- {
- /* If the user is working in another window right now, then don't
- steal focus */
- if (event_last_user_time && launch_time &&
- event_time_after(event_last_user_time, launch_time) &&
- event_last_user_time != launch_time &&
- event_time_after(event_last_user_time,
- map_time - OB_EVENT_USER_TIME_DELAY))
- {
- activate = FALSE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because the user is "
- "working in another window");
- }
- /* If its a transient (and its parents aren't focused) */
- else if (client_has_parent(self)) {
- activate = FALSE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because it is a "
- "transient, and its relatives aren't focused");
- }
- /* Don't steal focus from globally active clients.
- I stole this idea from KWin. It seems nice.
- */
- else if (!(focus_client->can_focus ||
- focus_client->focus_notify))
- {
- activate = FALSE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because a globally "
- "active client has focus");
- }
- /* Don't move focus if it's not going to go to this window
- anyway */
- else if (client_focus_target(self) != self) {
- activate = FALSE;
- raise = TRUE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because another window "
- "would get the focus anyway");
- }
- else if (!(self->desktop == screen_desktop ||
- self->desktop == DESKTOP_ALL))
- {
- activate = FALSE;
- raise = TRUE;
- ob_debug_type(OB_DEBUG_FOCUS,
- "Not focusing the window because it is on "
- "another desktop and no relatives are focused ");
- }
- }
+ activate = client_can_steal_focus(self, settings->focus,
+ event_time(), launch_time);
if (!activate) {
- ob_debug_type(OB_DEBUG_FOCUS,
- "Focus stealing prevention activated for %s at "
- "time %u (last user interactioon time %u)",
- self->title, map_time, event_last_user_time);
- /* if the client isn't focused, then hilite it so the user
- knows it is there */
- client_hilite(self, TRUE);
- /* we may want to raise it even tho we're not activating it */
- if (raise && !client_restore_session_stacking(self))
- stacking_raise(CLIENT_AS_WINDOW(self));
+ /* if the client isn't stealing focus, then hilite it so the user
+ knows it is there, but don't do this if we're restoring from a
+ session */
+ if (!client_restore_session_stacking(self))
+ client_hilite(self, TRUE);
}
}
else {
client_set_list();
/* free the ObAppSettings shallow copy */
- g_free(settings);
+ g_slice_free(ObAppSettings, settings);
ob_debug("Managed window 0x%lx plate 0x%x (%s)",
window, self->frame->window, self->class);
}
-
ObClient *client_fake_manage(Window window)
{
ObClient *self;
/* do this minimal stuff to figure out the client's decorations */
- self = g_new0(ObClient, 1);
+ self = g_slice_new0(ObClient);
self->window = window;
client_get_all(self, FALSE);
uses too. this returns a shallow copy that needs to be freed */
settings = client_get_settings_state(self);
- client_setup_decor_and_functions(self, FALSE);
-
/* create the decoration frame for the client window and adjust its size */
self->frame = frame_new(self);
- frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
+
+ client_apply_startup_state(self, self->area.x, self->area.y,
+ self->area.width, self->area.height);
ob_debug("gave extents left %d right %d top %d bottom %d",
self->frame->size.left, self->frame->size.right,
self->frame->size.top, self->frame->size.bottom);
/* free the ObAppSettings shallow copy */
- g_free(settings);
+ g_slice_free(ObAppSettings, settings);
return self;
}
void client_unmanage_all(void)
{
- while (client_list != NULL)
+ while (client_list)
client_unmanage(client_list->data);
}
mouse_grab_for_client(self, FALSE);
+ self->managed = FALSE;
+
/* remove the window from our save set, unless we are managing an internal
ObPrompt window */
if (!self->prompt)
g_free(self->role);
g_free(self->client_machine);
g_free(self->sm_client_id);
- g_free(self);
+ g_slice_free(ObClient, self);
}
void client_fake_unmanage(ObClient *self)
/* this is all that got allocated to get the decorations */
frame_free(self->frame);
- g_free(self);
+ g_slice_free(ObClient, self);
+}
+
+static gboolean client_can_steal_focus(ObClient *self,
+ gboolean allow_other_desktop,
+ Time steal_time,
+ Time launch_time)
+{
+ gboolean steal;
+ gboolean relative_focused;
+ gboolean parent_focused;
+
+ steal = TRUE;
+
+ parent_focused = (focus_client != NULL &&
+ client_search_focus_parent(self));
+ relative_focused = (focus_client != NULL &&
+ (client_search_focus_tree_full(self) != NULL ||
+ client_search_focus_group_full(self) != NULL));
+
+ /* This is focus stealing prevention */
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Want to focus window 0x%x at time %u "
+ "launched at %u (last user interaction time %u)",
+ self->window, steal_time, launch_time,
+ event_last_user_time);
+
+ /* if it's on another desktop... */
+ if (!(self->desktop == screen_desktop ||
+ self->desktop == DESKTOP_ALL) &&
+ /* and (we dont know when it launched, and we don't want to allow
+ focus stealing from other desktops */
+ ((!launch_time && !allow_other_desktop) ||
+ /* or the timestamp is from before you changed desktops) */
+ (screen_desktop_user_time &&
+ !event_time_after(launch_time, screen_desktop_user_time))))
+ {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because its on another "
+ "desktop\n");
+ }
+ /* If something is focused... */
+ else if (focus_client) {
+ /* If the user is working in another window right now, then don't
+ steal focus */
+ if (!parent_focused &&
+ event_last_user_time && launch_time &&
+ event_time_after(event_last_user_time, launch_time) &&
+ event_last_user_time != launch_time &&
+ event_time_after(event_last_user_time,
+ steal_time - OB_EVENT_USER_TIME_DELAY))
+ {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because the user is "
+ "working in another window that is not "
+ "its parent");
+ }
+ /* If the new window is a transient (and its relatives aren't
+ focused) */
+ else if (client_has_parent(self) && !relative_focused) {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because it is a "
+ "transient, and its relatives aren't focused");
+ }
+ /* Don't steal focus from globally active clients.
+ I stole this idea from KWin. It seems nice.
+ */
+ else if (!(focus_client->can_focus ||
+ focus_client->focus_notify))
+ {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because a globally "
+ "active client has focus");
+ }
+ /* Don't move focus if it's not going to go to this window
+ anyway */
+ else if (client_focus_target(self) != self) {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because another window "
+ "would get the focus anyway");
+ }
+ /* Don't move focus if the window is not visible on the current
+ desktop and none of its relatives are focused */
+ else if (!(self->desktop == screen_desktop ||
+ self->desktop == DESKTOP_ALL) &&
+ !relative_focused)
+ {
+ steal = FALSE;
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Not focusing the window because it is on "
+ "another desktop and no relatives are focused ");
+ }
+ }
+
+ if (!steal)
+ ob_debug_type(OB_DEBUG_FOCUS,
+ "Focus stealing prevention activated for %s at "
+ "time %u (last user interaction time %u)",
+ self->title, steal_time, event_last_user_time);
+ return steal;
}
/*! Returns a new structure containing the per-app settings for this client.
ObAppSettings *app = it->data;
gboolean match = TRUE;
- g_assert(app->name != NULL || app->class != NULL);
+ g_assert(app->name != NULL || app->class != NULL ||
+ app->role != NULL || app->title != NULL ||
+ (signed)app->type >= 0);
- /* we know that either name or class is not NULL so it will have to
- match to use the rule */
if (app->name &&
!g_pattern_match(app->name, strlen(self->name), self->name, NULL))
match = FALSE;
!g_pattern_match(app->role,
strlen(self->role), self->role, NULL))
match = FALSE;
- else if ((signed)app->type >= 0 && app->type != self->type)
+ else if (app->title &&
+ !g_pattern_match(app->title,
+ strlen(self->title), self->title, NULL))
+ match = FALSE;
+ else if ((signed)app->type >= 0 && app->type != self->type) {
match = FALSE;
+ }
if (match) {
ob_debug("Window matching: %s", app->name);
/* This makes sure windows aren't entirely outside of the screen so you
can't see them at all.
- It makes sure 10% of the window is on the screen at least. At don't
+ It makes sure 10% of the window is on the screen at least. And don't
let it move itself off the top of the screen, which would hide the
titlebar on you. (The user can still do this if they want too, it's
only limiting the application.
if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
*y = a->y + MAX(0, a->height - fh);
- g_free(a);
+ g_slice_free(Rect, a);
}
/* get where the client should be */
client_get_mwm_hints(self);
/* this can change the mwmhints for special cases */
client_get_type_and_transientness(self);
- client_get_state(self);
client_update_normal_hints(self);
+ /* set up the decor/functions before getting the state. the states may
+ affect which functions are available, but we want to know the maximum
+ decor/functions are available to this window, so we can then apply them
+ in client_apply_startup_state() */
+ client_setup_decor_and_functions(self, FALSE);
+
+ client_get_state(self);
+
/* get the session related properties, these can change decorations
from per-app settings */
client_get_session_ids(self);
/* get this early so we have it for debugging */
client_update_title(self);
+ /* save the values of the variables used for app rule matching */
+ client_save_app_rule_values(self);
+
client_update_protocols(self);
client_update_wmhints(self);
static void client_get_shaped(ObClient *self)
{
self->shaped = FALSE;
-#ifdef SHAPE
+#ifdef SHAPE
if (obt_display_extension_shape) {
gint foo;
guint ufoo;
XShapeQueryExtents(obt_display, self->window, &s, &foo,
&foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
&ufoo);
- self->shaped = (s != 0);
+ self->shaped = !!s;
}
#endif
}
gboolean trangroup = FALSE;
if (XGetTransientForHint(obt_display, self->window, &t)) {
- if (t != self->window) { /* cant be transient to itself! */
+ if (t != self->window) { /* can't be transient to itself! */
ObWindow *tw = window_find(t);
- /* if this happens then we need to check for it*/
+ /* if this happens then we need to check for it */
g_assert(tw != CLIENT_AS_WINDOW(self));
if (tw && WINDOW_IS_CLIENT(tw)) {
/* watch out for windows with a parent that is something
transient windows as their children.
* * */
-
/* No change has occured */
if (oldgroup == newgroup &&
oldgtran == newgtran &&
/* If we are now transient for a single window we need to add ourselves to
its children
- WARNING: Cyclical transient ness is possible if two windows are
+ WARNING: Cyclical transient-ness is possible if two windows are
transient for eachother.
*/
else if (newparent &&
}
/** If we change our group transient-ness, our children change their
- effect group transient-ness, which affects how they relate to other
+ effective group transient-ness, which affects how they relate to other
group windows **/
for (it = self->transients; it; it = g_slist_next(it)) {
}
}
-static void client_get_mwm_hints(ObClient *self)
+void client_get_mwm_hints(ObClient *self)
{
guint num;
guint32 *hints;
if (OBT_PROP_GET32(self->window, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
{
+ XSyncValue val;
+
self->sync_counter = i;
+
+ /* this must be set when managing a new window according to EWMH */
+ XSyncIntToValue(&val, 0);
+ XSyncSetCounter(obt_display, self->sync_counter, val);
} else
self->sync_counter = None;
}
if (client_focused(self)) {
screen_install_colormap(self, FALSE); /* uninstall old one */
self->colormap = colormap;
- screen_install_colormap(self, FALSE); /* install new one */
+ screen_install_colormap(self, TRUE); /* install new one */
} else
self->colormap = colormap;
}
self->min_ratio = 0.0f;
self->max_ratio = 0.0f;
SIZE_SET(self->size_inc, 1, 1);
- SIZE_SET(self->base_size, 0, 0);
+ SIZE_SET(self->base_size, -1, -1);
SIZE_SET(self->min_size, 0, 0);
SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
break;
}
+ /* If the client has no decor from its type (which never changes) then
+ don't allow the user to "undecorate" the window. Otherwise, allow them
+ to, even if there are motif hints removing the decor, because those
+ may change these days (e.g. chromium) */
+ if (self->decorations == 0)
+ self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
+
/* Mwm Hints are applied subtractively to what has already been chosen for
decor and functionality */
if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
}
if (self->max_horz && self->max_vert) {
- /* you can't resize fully maximized windows */
- self->functions &= ~OB_CLIENT_FUNC_RESIZE;
- /* kill the handle on fully maxed windows */
+ /* once upon a time you couldn't resize maximized windows, that is not
+ the case any more though !
+
+ but do kill the handle on fully maxed windows */
self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
}
- /* If there are no decorations to remove, don't allow the user to try
- toggle the state */
- if (self->decorations == 0)
- self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
-
/* finally, the user can have requested no decorations, which overrides
everything (but doesnt give it a border if it doesnt have one) */
if (self->undecorated)
- self->decorations = 0;
+ self->decorations &= (config_theme_keepborder ?
+ OB_FRAME_DECOR_BORDER : 0);
/* if we don't have a titlebar, then we cannot shade! */
if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
client_change_allowed_actions(self);
if (reconfig)
- /* force reconfigure to make sure decorations are updated */
- client_reconfigure(self, TRUE);
+ /* reconfigure to make sure decorations are updated */
+ client_reconfigure(self, FALSE);
}
static void client_change_allowed_actions(ObClient *self)
{
XWMHints *hints;
- /* assume a window takes input if it doesnt specify */
+ /* assume a window takes input if it doesn't specify */
self->can_focus = TRUE;
if ((hints = XGetWMHints(obt_display, self->window)) != NULL) {
ObGroup *oldgroup = self->group;
/* remove from the old group if there was one */
- if (self->group != NULL) {
+ if (self->group) {
group_remove(self->group, self);
self->group = NULL;
}
XFree(hints);
}
+
+ focus_cycle_addremove(self, TRUE);
}
void client_update_title(ObClient *self)
*/
data = g_strdup("");
} else
- data = g_strdup("Unnamed Window");
+ data = g_strdup(_("Unnamed Window"));
}
}
self->original_title = g_strdup(data);
if (!got &&
OBT_PROP_GETA32(self->window, NET_WM_STRUT, CARDINAL, &data, &num)) {
if (num == 4) {
- Rect *a;
+ const Rect *a;
got = TRUE;
a->x, a->x + a->width - 1,
a->y, a->y + a->height - 1,
a->x, a->x + a->width - 1);
- g_free(a);
}
g_free(data);
}
STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0);
- if (!STRUT_EQUAL(strut, self->strut)) {
+ if (!PARTIAL_STRUT_EQUAL(strut, self->strut)) {
self->strut = strut;
/* updating here is pointless while we're being mapped cuz we're not in
(gint*)&w, (gint*)&h, &data);
obt_display_ignore_errors(FALSE);
-
if (xicon) {
if (w > 0 && h > 0) {
/* is this icon in the cache yet? */
}
}
+/*! Save the properties used for app matching rules, as seen by Openbox when
+ the window mapped, so that users can still access them later if the app
+ changes them */
+static void client_save_app_rule_values(ObClient *self)
+{
+ const gchar *type;
+
+ OBT_PROP_SETS(self->window, OB_APP_ROLE, utf8, self->role);
+ OBT_PROP_SETS(self->window, OB_APP_NAME, utf8, self->name);
+ OBT_PROP_SETS(self->window, OB_APP_CLASS, utf8, self->class);
+ OBT_PROP_SETS(self->window, OB_APP_TITLE, utf8, self->original_title);
+
+ switch (self->type) {
+ case OB_CLIENT_TYPE_NORMAL:
+ type = "normal"; break;
+ case OB_CLIENT_TYPE_DIALOG:
+ type = "dialog"; break;
+ case OB_CLIENT_TYPE_UTILITY:
+ type = "utility"; break;
+ case OB_CLIENT_TYPE_MENU:
+ type = "menu"; break;
+ case OB_CLIENT_TYPE_TOOLBAR:
+ type = "toolbar"; break;
+ case OB_CLIENT_TYPE_SPLASH:
+ type = "splash"; break;
+ case OB_CLIENT_TYPE_DESKTOP:
+ type = "desktop"; break;
+ case OB_CLIENT_TYPE_DOCK:
+ type = "dock"; break;
+ }
+ OBT_PROP_SETS(self->window, OB_APP_TYPE, utf8, type);
+}
+
static void client_change_wm_state(ObClient *self)
{
gulong state[2];
for (it = self->parents; it; it = g_slist_next(it)) {
ObClient *c = it->data;
- if ((c = client_search_focus_tree_full(it->data))) return c;
+ if ((c = client_search_focus_tree_full(c))) return c;
}
return NULL;
return self->parents != NULL;
}
+gboolean client_is_oldfullscreen(const ObClient *self,
+ const Rect *area)
+{
+ const Rect *monitor, *allmonitors;
+
+ /* No decorations and fills the monitor = oldskool fullscreen.
+ But not for maximized windows.
+ */
+
+ if (self->decorations || self->max_horz || self->max_vert) return FALSE;
+
+ monitor = screen_physical_area_monitor(screen_find_monitor(area));
+ allmonitors = screen_physical_area_all_monitors();
+
+ return (RECT_EQUAL(*area, *monitor) ||
+ RECT_EQUAL(*area, *allmonitors));
+}
+
static ObStackingLayer calc_layer(ObClient *self)
{
ObStackingLayer l;
- Rect *monitor;
-
- monitor = screen_physical_area_monitor(client_monitor(self));
if (self->type == OB_CLIENT_TYPE_DESKTOP)
l = OB_STACKING_LAYER_DESKTOP;
else l = OB_STACKING_LAYER_ABOVE;
}
else if ((self->fullscreen ||
- /* No decorations and fills the monitor = oldskool fullscreen.
- But not for maximized windows.
- */
- (self->decorations == 0 &&
- !(self->max_horz && self->max_vert) &&
- RECT_EQUAL(self->area, *monitor))) &&
+ client_is_oldfullscreen(self, &self->area)) &&
/* you are fullscreen while you or your children are focused.. */
(client_focused(self) || client_search_focus_tree(self) ||
/* you can be fullscreen if you're on another desktop */
else if (self->below) l = OB_STACKING_LAYER_BELOW;
else l = OB_STACKING_LAYER_NORMAL;
- g_free(monitor);
-
return l;
}
gboolean hide = FALSE;
if (!client_should_show(self)) {
- if (self == focus_client) {
- /* if there is a grab going on, then we need to cancel it. if we
- move focus during the grab, applications will get
- NotifyWhileGrabbed events and ignore them !
-
- actions should not rely on being able to move focus during an
- interactive grab.
- */
- event_cancel_all_key_grabs();
- }
-
/* We don't need to ignore enter events here.
The window can hide/iconify in 3 different ways:
1 - through an x message. in this case we ignore all enter events
self->type != OB_CLIENT_TYPE_DESKTOP);
}
-
static void client_apply_startup_state(ObClient *self,
gint x, gint y, gint w, gint h)
{
if (iconic)
client_iconify(self, TRUE, FALSE, TRUE);
- if (fullscreen)
- client_fullscreen(self, TRUE);
if (undecorated)
client_set_undecorated(self, TRUE);
if (shaded)
else if (max_horz)
client_maximize(self, TRUE, 1);
+ /* fullscreen removes the ability to apply other states */
+ if (fullscreen)
+ client_fullscreen(self, TRUE);
+
/* if the window hasn't been configured yet, then do so now, in fact the
x,y,w,h may _not_ be the same as the area rect, which can end up
meaning that the client isn't properly moved/resized by the fullscreen
Rect desired = {*x, *y, *w, *h};
frame_rect_to_frame(self->frame, &desired);
- /* make the frame recalculate its dimentions n shit without changing
+ /* make the frame recalculate its dimensions n shit without changing
anything visible for real, this way the constraints below can work with
the updated frame dimensions. */
frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
/* set the size and position if fullscreen */
if (self->fullscreen) {
- Rect *a;
+ const Rect *a;
guint i;
i = screen_find_monitor(&desired);
user = FALSE; /* ignore if the client can't be moved/resized when it
is fullscreening */
-
- g_free(a);
} else if (self->max_horz || self->max_vert) {
Rect *a;
guint i;
user = FALSE; /* ignore if the client can't be moved/resized when it
is maximizing */
- g_free(a);
+ g_slice_free(Rect, a);
}
/* gets the client's position */
frame_frame_gravity(self->frame, x, y);
- /* work within the prefered sizes given by the window */
- if (!(*w == self->area.width && *h == self->area.height)) {
+ /* work within the preferred sizes given by the window, these may have
+ changed rather than it's requested width and height, so always run
+ through this code */
+ {
gint basew, baseh, minw, minh;
- gint incw, inch;
+ gint incw, inch, maxw, maxh;
gfloat minratio, maxratio;
- incw = self->fullscreen || self->max_horz ? 1 : self->size_inc.width;
- inch = self->fullscreen || self->max_vert ? 1 : self->size_inc.height;
+ incw = self->size_inc.width;
+ inch = self->size_inc.height;
minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
0 : self->min_ratio;
maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
0 : self->max_ratio;
/* base size is substituted with min size if not specified */
- if (self->base_size.width || self->base_size.height) {
+ if (self->base_size.width >= 0 || self->base_size.height >= 0) {
basew = self->base_size.width;
baseh = self->base_size.height;
} else {
minh = self->base_size.height;
}
+ /* This comment is no longer true */
/* if this is a user-requested resize, then check against min/max
sizes */
*w -= basew;
*h -= baseh;
+ /* the sizes to used for maximized */
+ maxw = *w;
+ maxh = *h;
+
/* keep to the increments */
*w /= incw;
*h /= inch;
*w *= incw;
*h *= inch;
+ /* if maximized/fs then don't use the size increments */
+ if (self->fullscreen || self->max_horz) *w = maxw;
+ if (self->fullscreen || self->max_vert) *h = maxh;
+
*w += basew;
*h += baseh;
g_assert(*h > 0);
}
-
void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
gboolean user, gboolean final, gboolean force_reply)
{
- Rect oldframe;
- gint oldw, oldh;
+ Rect oldframe, oldclient;
gboolean send_resize_client;
gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
gboolean fmoved, fresized;
moved = (x != self->area.x || y != self->area.y);
resized = (w != self->area.width || h != self->area.height);
- oldw = self->area.width;
- oldh = self->area.height;
oldframe = self->frame->area;
+ oldclient = self->area;
RECT_SET(self->area, x, y, w, h);
/* for app-requested resizes, always resize if 'resized' is true.
(resized && config_resize_redraw))));
/* if the client is enlarging, then resize the client before the frame */
- if (send_resize_client && (w > oldw || h > oldh)) {
+ if (send_resize_client && (w > oldclient.width || h > oldclient.height)) {
XMoveResizeWindow(obt_display, self->window,
self->frame->size.left, self->frame->size.top,
- MAX(w, oldw), MAX(h, oldh));
+ MAX(w, oldclient.width), MAX(h, oldclient.height));
frame_adjust_client_area(self->frame);
}
When user = FALSE, then the request is coming from the application
itself, and we are more strict about when to send a synthetic
ConfigureNotify. We strictly follow the rules of the ICCCM sec 4.1.5
- in this case (if force_reply is true)
+ in this case (or send one if force_reply is true)
When user = TRUE, then the request is coming from "us", like when we
maximize a window or something. In this case we are more lenient. We
used to follow the same rules as above, but _Java_ Swing can't handle
this. So just to appease Swing, when user = TRUE, we always send
a synthetic ConfigureNotify to give the window its root coordinates.
+ Lastly, if force_reply is TRUE, we always send a
+ ConfigureNotify, which is needed during a resize with XSYNCronization.
*/
if ((!user && !resized && (rootmoved || force_reply)) ||
- (user && final && rootmoved))
+ (user && ((!resized && force_reply) || (final && rootmoved))))
{
XEvent event;
both of these resize sections may run, because the top one only resizes
in the direction that is growing
*/
- if (send_resize_client && (w <= oldw || h <= oldh)) {
+ if (send_resize_client && (w <= oldclient.width || h <= oldclient.height))
+ {
frame_adjust_client_area(self->frame);
XMoveResizeWindow(obt_display, self->window,
self->frame->size.left, self->frame->size.top, w, h);
XFlush(obt_display);
/* if it moved between monitors, then this can affect the stacking
- layer of this window or others - for fullscreen windows */
- if (screen_find_monitor(&self->frame->area) !=
- screen_find_monitor(&oldframe))
+ layer of this window or others - for fullscreen windows.
+ also if it changed to/from oldschool fullscreen then its layer may
+ change
+
+ watch out tho, don't try change stacking stuff if the window is no
+ longer being managed !
+ */
+ if (self->managed &&
+ (screen_find_monitor(&self->frame->area) !=
+ screen_find_monitor(&oldframe) ||
+ (final && (client_is_oldfullscreen(self, &oldclient) !=
+ client_is_oldfullscreen(self, &self->area)))))
{
client_calc_layer(self);
}
if (fs) {
self->pre_fullscreen_area = self->area;
+ self->pre_fullscreen_max_horz = self->max_horz;
+ self->pre_fullscreen_max_vert = self->max_vert;
+
/* if the window is maximized, its area isn't all that meaningful.
- save it's premax area instead. */
+ save its premax area instead. */
if (self->max_horz) {
self->pre_fullscreen_area.x = self->pre_max_area.x;
self->pre_fullscreen_area.width = self->pre_max_area.width;
g_assert(self->pre_fullscreen_area.width > 0 &&
self->pre_fullscreen_area.height > 0);
+ self->max_horz = self->pre_fullscreen_max_horz;
+ self->max_vert = self->pre_fullscreen_max_vert;
+ if (self->max_horz) {
+ self->pre_max_area.x = self->pre_fullscreen_area.x;
+ self->pre_max_area.width = self->pre_fullscreen_area.width;
+ }
+ if (self->max_vert) {
+ self->pre_max_area.y = self->pre_fullscreen_area.y;
+ self->pre_max_area.height = self->pre_fullscreen_area.height;
+ }
+
x = self->pre_fullscreen_area.x;
y = self->pre_fullscreen_area.y;
w = self->pre_fullscreen_area.width;
ob_debug("Window %s going fullscreen (%d)",
self->title, self->fullscreen);
+ if (fs) {
+ /* make sure the window is on some monitor */
+ client_find_onscreen(self, &x, &y, w, h, FALSE);
+ }
+
client_setup_decor_and_functions(self, FALSE);
client_move_resize(self, x, y, w, h);
GSList *it;
gboolean changed = FALSE;
-
if (self->iconic != iconic) {
ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
self->window);
self->iconic = iconic;
/* update the focus lists.. iconic windows go to the bottom of
- the list */
+ the list. this will also call focus_cycle_addremove(). */
focus_order_to_bottom(self);
changed = TRUE;
self->desktop != DESKTOP_ALL)
client_set_desktop(self, screen_desktop, FALSE, FALSE);
- /* this puts it after the current focused window */
- focus_order_remove(self);
- focus_order_add_new(self);
+ /* this puts it after the current focused window, this will
+ also cause focus_cycle_addremove() to be called for the
+ client */
+ focus_order_like_new(self);
changed = TRUE;
}
gint x, y, w, h;
g_assert(dir == 0 || dir == 1 || dir == 2);
- if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
+ if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && max) return;/* can't */
/* check if already done */
if (max) {
if (dir == 0 || dir == 2) /* vert */
self->max_vert = max;
+ if (max) {
+ /* make sure the window is on some monitor */
+ client_find_onscreen(self, &x, &y, w, h, FALSE);
+ }
+
client_change_state(self); /* change the state hints on the client */
client_setup_decor_and_functions(self, FALSE);
else {
/* request the client to close with WM_DELETE_WINDOW */
OBT_PROP_MSG_TO(self->window, self->window, WM_PROTOCOLS,
- OBT_PROP_ATOM(WM_DELETE_WINDOW), event_curtime,
+ OBT_PROP_ATOM(WM_DELETE_WINDOW), event_time(),
0, 0, 0, NoEventMask);
/* we're trying to close the window, so see if it is responding. if it
#define OB_KILL_RESULT_NO 0
#define OB_KILL_RESULT_YES 1
-static void client_kill_requested(ObPrompt *p, gint result, gpointer data)
+static gboolean client_kill_requested(ObPrompt *p, gint result, gpointer data)
{
ObClient *self = data;
if (result == OB_KILL_RESULT_YES)
client_kill(self);
+ return TRUE; /* call the cleanup func */
+}
+
+static void client_kill_cleanup(ObPrompt *p, gpointer data)
+{
+ ObClient *self = data;
+
+ g_assert(p == self->kill_prompt);
prompt_unref(self->kill_prompt);
self->kill_prompt = NULL;
/* check if we're already prompting */
if (!self->kill_prompt) {
ObPromptAnswer answers[] = {
- { _("No"), OB_KILL_RESULT_NO },
- { _("Yes"), OB_KILL_RESULT_YES }
+ { 0, OB_KILL_RESULT_NO },
+ { 0, OB_KILL_RESULT_YES }
};
gchar *m;
+ const gchar *y, *title;
+
+ title = self->original_title;
+ if (title[0] == '\0') {
+ /* empty string, so use its parent */
+ ObClient *p = client_search_top_direct_parent(self);
+ if (p) title = p->original_title;
+ }
if (client_on_localhost(self)) {
const gchar *sig;
sig = "kill";
m = g_strdup_printf
- (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"), self->original_title, sig);
+ (_("The window \"%s\" does not seem to be responding. Do you want to force it to exit by sending the %s signal?"),
+ title, sig);
+ y = _("End Process");
}
- else
+ else {
m = g_strdup_printf
- (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"), self->original_title);
-
+ (_("The window \"%s\" does not seem to be responding. Do you want to disconnect it from the X server?"),
+ title);
+ y = _("Disconnect");
+ }
+ /* set the dialog buttons' text */
+ answers[0].text = _("Cancel"); /* "no" */
+ answers[1].text = y; /* "yes" */
- self->kill_prompt = prompt_new(m, answers,
+ self->kill_prompt = prompt_new(m, NULL, answers,
sizeof(answers)/sizeof(answers[0]),
OB_KILL_RESULT_NO, /* default = no */
OB_KILL_RESULT_NO, /* cancel = no */
- client_kill_requested, self);
+ client_kill_requested,
+ client_kill_cleanup,
+ self);
g_free(m);
}
- prompt_show(self->kill_prompt, self);
+ prompt_show(self->kill_prompt, self, TRUE);
}
void client_kill(ObClient *self)
/* don't allow focused windows to hilite */
self->demands_attention = hilite && !client_focused(self);
if (self->frame != NULL) { /* if we're mapping, just set the state */
- if (self->demands_attention)
+ if (self->demands_attention) {
frame_flash_start(self->frame);
+
+ /* if the window is on another desktop then raise it and make it
+ the most recently used window */
+ if (self->desktop != screen_desktop &&
+ self->desktop != DESKTOP_ALL)
+ {
+ stacking_raise(CLIENT_AS_WINDOW(self));
+ focus_order_to_top(self);
+ }
+ }
else
frame_flash_stop(self->frame);
client_change_state(self);
/* the new desktop's geometry may be different, so we may need to
resize, for example if we are maximized */
client_reconfigure(self, FALSE);
+
+ focus_cycle_addremove(self, FALSE);
}
/* move all transients */
{
self = client_search_top_direct_parent(self);
client_set_desktop_recursive(self, target, donthide, dontraise);
+
+ focus_cycle_addremove(NULL, TRUE);
}
gboolean client_is_direct_child(ObClient *parent, ObClient *child)
return NULL;
}
+struct ObClientFindDestroyUnmap {
+ Window window;
+ gint ignore_unmaps;
+};
+
+static gboolean find_destroy_unmap(XEvent *e, gpointer data)
+{
+ struct ObClientFindDestroyUnmap *find = data;
+ if (e->type == DestroyNotify)
+ return e->xdestroywindow.window == find->window;
+ if (e->type == UnmapNotify && e->xunmap.window == find->window)
+ /* ignore the first $find->ignore_unmaps$ many unmap events */
+ return --find->ignore_unmaps < 0;
+ return FALSE;
+}
+
gboolean client_validate(ObClient *self)
{
- XEvent e;
+ struct ObClientFindDestroyUnmap find;
XSync(obt_display, FALSE); /* get all events on the server */
- if (XCheckTypedWindowEvent(obt_display, self->window, DestroyNotify, &e) ||
- XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e))
- {
- XPutBackEvent(obt_display, &e);
+ find.window = self->window;
+ find.ignore_unmaps = self->ignore_unmaps;
+ if (xqueue_exists_local(find_destroy_unmap, &find))
return FALSE;
- }
return TRUE;
}
gboolean above = self->above;
gboolean below = self->below;
gint i;
+ gboolean value;
if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
/* if toggling, then pick whether we're adding or removing */
if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
- action = modal ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = modal;
else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
- action = self->max_vert ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = self->max_vert;
else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
- action = self->max_horz ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = self->max_horz;
else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
- action = shaded ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = shaded;
else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
- action = self->skip_taskbar ?
- OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = self->skip_taskbar;
else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
- action = self->skip_pager ?
- OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = self->skip_pager;
else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
- action = self->iconic ?
- OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = self->iconic;
else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
- action = fullscreen ?
- OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = fullscreen;
else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
- action = self->above ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = self->above;
else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
- action = self->below ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = self->below;
else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
- action = self->demands_attention ?
- OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = self->demands_attention;
else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
- action = undecorated ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
- OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ value = undecorated;
+ else
+ g_assert_not_reached();
+ action = value ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
+ OBT_PROP_ATOM(NET_WM_STATE_ADD);
}
- if (action == OBT_PROP_ATOM(NET_WM_STATE_ADD)) {
- if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
- modal = TRUE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
- max_vert = TRUE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
- max_horz = TRUE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
- shaded = TRUE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
- self->skip_taskbar = TRUE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
- self->skip_pager = TRUE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
- iconic = TRUE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
- fullscreen = TRUE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
- above = TRUE;
+ value = action == OBT_PROP_ATOM(NET_WM_STATE_ADD);
+ if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
+ modal = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
+ max_vert = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
+ max_horz = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
+ shaded = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
+ self->skip_taskbar = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
+ self->skip_pager = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
+ iconic = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
+ fullscreen = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
+ above = value;
+ /* only unset below when setting above, otherwise you can't get to
+ the normal layer */
+ if (value)
below = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
- above = FALSE;
- below = TRUE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
- demands_attention = TRUE;
- } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
- undecorated = TRUE;
- }
-
- } else { /* action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) */
- if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
- modal = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
- max_vert = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
- max_horz = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
- shaded = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
- self->skip_taskbar = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
- self->skip_pager = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
- iconic = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
- fullscreen = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
+ /* and vice versa */
+ if (value)
above = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
- below = FALSE;
- } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
- demands_attention = FALSE;
- } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
- undecorated = FALSE;
- }
+ below = value;
+ } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
+ demands_attention = value;
+ } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
+ undecorated = value;
}
}
client_hilite(self, demands_attention);
client_change_state(self); /* change the hint to reflect these changes */
+
+ focus_cycle_addremove(self, TRUE);
}
ObClient *client_focus_target(ObClient *self)
gboolean client_focus(ObClient *self)
{
+ if (!client_validate(self)) return FALSE;
+
/* we might not focus this window, so if we have modal children which would
be focused instead, bring them to this desktop */
client_bring_modal_windows(self);
ob_debug_type(OB_DEBUG_FOCUS,
"Focusing client \"%s\" (0x%x) at time %u",
- self->title, self->window, event_curtime);
+ self->title, self->window, event_time());
/* if using focus_delay, stop the timer now so that focus doesn't
go moving on us */
event_halt_focus_delay();
- /* if there is a grab going on, then we need to cancel it. if we move
- focus during the grab, applications will get NotifyWhileGrabbed events
- and ignore them !
-
- actions should not rely on being able to move focus during an
- interactive grab.
- */
- event_cancel_all_key_grabs();
-
obt_display_ignore_errors(TRUE);
if (self->can_focus) {
/* This can cause a BadMatch error with CurrentTime, or if an app
passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
XSetInputFocus(obt_display, self->window, RevertToPointerRoot,
- event_curtime);
+ event_time());
}
if (self->focus_notify) {
ce.xclient.window = self->window;
ce.xclient.format = 32;
ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
- ce.xclient.data.l[1] = event_curtime;
+ ce.xclient.data.l[1] = event_time();
ce.xclient.data.l[2] = 0l;
ce.xclient.data.l[3] = 0l;
ce.xclient.data.l[4] = 0l;
client_focus(self);
}
-void client_activate(ObClient *self, gboolean here, gboolean raise,
+/* this function exists to map to the net_active_window message in the ewmh */
+void client_activate(ObClient *self, gboolean desktop,
+ gboolean here, gboolean raise,
gboolean unshade, gboolean user)
{
- client_present(self, here, raise, unshade);
+ if ((user && (desktop ||
+ self->desktop == DESKTOP_ALL ||
+ self->desktop == screen_desktop)) ||
+ client_can_steal_focus(self, desktop, event_time(), CurrentTime))
+ {
+ client_present(self, here, raise, unshade);
+ }
+ else
+ client_hilite(self, TRUE);
}
static void client_bring_windows_recursive(ObClient *self,
return self == focus_client;
}
-
-
RrImage* client_icon(ObClient *self)
{
RrImage *ret = NULL;
return NULL;
}
+ObClient *client_search_focus_parent_full(ObClient *self)
+{
+ GSList *it;
+ ObClient *ret = NULL;
+
+ for (it = self->parents; it; it = g_slist_next(it)) {
+ if (client_focused(it->data))
+ ret = it->data;
+ else
+ ret = client_search_focus_parent_full(it->data);
+ if (ret) break;
+ }
+ return ret;
+}
+
ObClient *client_search_parent(ObClient *self, ObClient *search)
{
GSList *it;
/* check if the head of this window is closer than the previously
chosen edge (take into account that the previously chosen
edge might have been a tail, not a head) */
- if (head + (*near_edge ? 0 : my_size) < *dest)
+ if (head + (*near_edge ? 0 : my_size) <= *dest)
skip_head = TRUE;
/* check if the tail of this window is closer than the previously
chosen edge (take into account that the previously chosen
edge might have been a head, not a tail) */
- if (tail - (!*near_edge ? 0 : my_size) < *dest)
+ if (tail - (!*near_edge ? 0 : my_size) <= *dest)
skip_tail = TRUE;
break;
case OB_DIRECTION_SOUTH:
/* check if the head of this window is closer than the previously
chosen edge (take into account that the previously chosen
edge might have been a tail, not a head) */
- if (head - (*near_edge ? 0 : my_size) > *dest)
+ if (head - (*near_edge ? 0 : my_size) >= *dest)
skip_head = TRUE;
/* check if the tail of this window is closer than the previously
chosen edge (take into account that the previously chosen
edge might have been a head, not a tail) */
- if (tail + (!*near_edge ? 0 : my_size) > *dest)
+ if (tail + (!*near_edge ? 0 : my_size) >= *dest)
skip_tail = TRUE;
break;
default:
}
ob_debug("my head %d size %d", my_head, my_size);
- ob_debug("head %d tail %d deest %d", head, tail, *dest);
+ ob_debug("head %d tail %d dest %d", head, tail, *dest);
if (!skip_head) {
ob_debug("using near edge %d", head);
*dest = head;
gint *dest, gboolean *near_edge)
{
GList *it;
- Rect *a, *mon;
+ Rect *a;
Rect dock_area;
gint edge;
+ guint i;
a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
&self->frame->area);
- mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR,
- &self->frame->area);
switch (dir) {
case OB_DIRECTION_NORTH:
- if (my_head >= RECT_TOP(*mon) + 1)
- edge = RECT_TOP(*mon) - 1;
- else
- edge = RECT_TOP(*a) - 1;
+ edge = RECT_TOP(*a) - 1;
break;
case OB_DIRECTION_SOUTH:
- if (my_head <= RECT_BOTTOM(*mon) - 1)
- edge = RECT_BOTTOM(*mon) + 1;
- else
- edge = RECT_BOTTOM(*a) + 1;
+ edge = RECT_BOTTOM(*a) + 1;
break;
case OB_DIRECTION_EAST:
- if (my_head <= RECT_RIGHT(*mon) - 1)
- edge = RECT_RIGHT(*mon) + 1;
- else
- edge = RECT_RIGHT(*a) + 1;
+ edge = RECT_RIGHT(*a) + 1;
break;
case OB_DIRECTION_WEST:
- if (my_head >= RECT_LEFT(*mon) + 1)
- edge = RECT_LEFT(*mon) - 1;
- else
- edge = RECT_LEFT(*a) - 1;
+ edge = RECT_LEFT(*a) - 1;
break;
default:
g_assert_not_reached();
*dest = edge;
*near_edge = TRUE;
- for (it = client_list; it; it = g_list_next(it)) {
- ObClient *cur = it->data;
+ /* search for edges of monitors */
+ for (i = 0; i < screen_num_monitors; ++i) {
+ Rect *area = screen_area(self->desktop, i, NULL);
+ detect_edge(*area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
+ g_slice_free(Rect, area);
+ }
- /* skip windows to not bump into */
- if (cur == self)
- continue;
- if (cur->iconic)
- continue;
- if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
- cur->desktop != screen_desktop)
- continue;
+ /* search for edges of clients */
+ if (((dir == OB_DIRECTION_NORTH || dir == OB_DIRECTION_SOUTH) &&
+ !self->max_vert) ||
+ ((dir == OB_DIRECTION_EAST || dir == OB_DIRECTION_WEST) &&
+ !self->max_horz))
+ {
+ for (it = client_list; it; it = g_list_next(it)) {
+ ObClient *cur = it->data;
- ob_debug("trying window %s", cur->title);
+ /* skip windows to not bump into */
+ if (cur == self)
+ continue;
+ if (cur->iconic)
+ continue;
+ if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
+ cur->desktop != screen_desktop)
+ continue;
- detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
+ ob_debug("trying window %s", cur->title);
+
+ detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
+ my_edge_size, dest, near_edge);
+ }
+ dock_get_area(&dock_area);
+ detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
my_edge_size, dest, near_edge);
}
- dock_get_area(&dock_area);
- detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
- my_edge_size, dest, near_edge);
- g_free(a);
- g_free(mon);
+
+ g_slice_free(Rect, a);
}
void client_find_move_directional(ObClient *self, ObDirection dir,
switch (side) {
case OB_DIRECTION_EAST:
head = RECT_RIGHT(self->frame->area) +
- (self->size_inc.width - 1) * (grow ? 1 : -1);
+ (self->size_inc.width - 1) * (grow ? 1 : 0);
e_start = RECT_TOP(self->frame->area);
e_size = self->frame->area.height;
dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
break;
case OB_DIRECTION_WEST:
head = RECT_LEFT(self->frame->area) -
- (self->size_inc.width - 1) * (grow ? 1 : -1);
+ (self->size_inc.width - 1) * (grow ? 1 : 0);
e_start = RECT_TOP(self->frame->area);
e_size = self->frame->area.height;
dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
break;
case OB_DIRECTION_NORTH:
head = RECT_TOP(self->frame->area) -
- (self->size_inc.height - 1) * (grow ? 1 : -1);
+ (self->size_inc.height - 1) * (grow ? 1 : 0);
e_start = RECT_LEFT(self->frame->area);
e_size = self->frame->area.width;
dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
break;
case OB_DIRECTION_SOUTH:
head = RECT_BOTTOM(self->frame->area) +
- (self->size_inc.height - 1) * (grow ? 1 : -1);
+ (self->size_inc.height - 1) * (grow ? 1 : 0);
e_start = RECT_LEFT(self->frame->area);
e_size = self->frame->area.width;
dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;