#include "grab.h"
#include "prompt.h"
#include "focus.h"
+#include "focus_cycle.h"
#include "stacking.h"
#include "openbox.h"
#include "group.h"
#include "obrender/render.h"
#include "gettext.h"
#include "obt/display.h"
+#include "obt/xqueue.h"
#include "obt/prop.h"
#ifdef HAVE_UNISTD_H
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_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_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, Time steal_time,
- Time launch_time);
+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", self->name, self->class, self->role);
+ ob_debug("Window name: %s class: %s role: %s title: %s",
+ self->name, self->class, self->role, self->title);
/* 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);
- /* 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);
-
/* specify that if we exit, the window should not be destroyed and
should be reparented back to root automatically, unless we are managing
an internal ObPrompt window */
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 = map_time;
+ user_time = event_time();
/* do this after we have a frame.. it uses the frame to help determine the
WM_STATE to apply. */
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) {
- activate = client_can_steal_focus(self, map_time, launch_time);
+ activate = client_can_steal_focus(self, settings->focus,
+ event_time(), launch_time);
if (!activate) {
/* if the client isn't stealing focus, then hilite it so the user
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);
/* 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;
}
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, Time steal_time,
+static gboolean client_can_steal_focus(ObClient *self,
+ gboolean allow_other_desktop,
+ Time steal_time,
Time launch_time)
{
gboolean steal;
/* This is focus stealing prevention */
ob_debug_type(OB_DEBUG_FOCUS,
- "Want to focus new window 0x%x at time %u "
+ "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 it's on another desktop... */
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))
+ /* 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,
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);
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);
- client_save_session_ids(self);
/* now we got everything that can affect the decorations */
if (!real)
/* 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_startup_id(ObClient *self)
{
- if (!(OBT_PROP_GETS(self->window, NET_STARTUP_ID, utf8,
- &self->startup_id)))
+ if (!(OBT_PROP_GETS_UTF8(self->window, NET_STARTUP_ID, &self->startup_id)))
if (self->group)
- OBT_PROP_GETS(self->group->leader,
- NET_STARTUP_ID, utf8, &self->startup_id);
+ OBT_PROP_GETS_UTF8(self->group->leader, NET_STARTUP_ID,
+ &self->startup_id);
}
static void client_get_area(ObClient *self)
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;
}
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)
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)
XFree(hints);
}
+
+ focus_cycle_addremove(self, TRUE);
}
void client_update_title(ObClient *self)
g_free(self->original_title);
/* try netwm */
- if (!OBT_PROP_GETS(self->window, NET_WM_NAME, utf8, &data)) {
+ if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_NAME, &data)) {
/* try old x stuff */
- if (!(OBT_PROP_GETS(self->window, WM_NAME, locale, &data)
- || OBT_PROP_GETS(self->window, WM_NAME, utf8, &data))) {
+ if (!OBT_PROP_GETS(self->window, WM_NAME, &data)) {
if (self->transient) {
- /*
- GNOME alert windows are not given titles:
- http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
- */
+ /*
+ GNOME alert windows are not given titles:
+ http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
+ */
data = g_strdup("");
} else
data = g_strdup(_("Unnamed Window"));
g_free(data);
}
- OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, utf8, visible);
+ OBT_PROP_SETS(self->window, NET_WM_VISIBLE_NAME, visible);
self->title = visible;
if (self->frame)
g_free(self->icon_title);
/* try netwm */
- if (!OBT_PROP_GETS(self->window, NET_WM_ICON_NAME, utf8, &data))
+ if (!OBT_PROP_GETS_UTF8(self->window, NET_WM_ICON_NAME, &data))
/* try old x stuff */
- if (!(OBT_PROP_GETS(self->window, WM_ICON_NAME, locale, &data) ||
- OBT_PROP_GETS(self->window, WM_ICON_NAME, utf8, &data)))
+ if (!OBT_PROP_GETS(self->window, WM_ICON_NAME, &data))
data = g_strdup(self->title);
if (self->client_machine) {
g_free(data);
}
- OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, utf8, visible);
+ OBT_PROP_SETS(self->window, NET_WM_VISIBLE_ICON_NAME, visible);
self->icon_title = visible;
}
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);
}
leader = None;
/* get the SM_CLIENT_ID */
- got = FALSE;
- if (leader)
- got = OBT_PROP_GETS(leader, SM_CLIENT_ID, locale, &self->sm_client_id);
- if (!got)
- OBT_PROP_GETS(self->window, SM_CLIENT_ID, locale, &self->sm_client_id);
+ if (leader && leader != self->window)
+ OBT_PROP_GETS_XPCS(leader, SM_CLIENT_ID, &self->sm_client_id);
+ else
+ OBT_PROP_GETS_XPCS(self->window, SM_CLIENT_ID, &self->sm_client_id);
/* get the WM_CLASS (name and class). make them "" if they are not
provided */
- got = FALSE;
- if (leader)
- got = OBT_PROP_GETSS(leader, WM_CLASS, locale, &ss);
- if (!got)
- got = OBT_PROP_GETSS(self->window, WM_CLASS, locale, &ss);
+ got = OBT_PROP_GETSS_TYPE(self->window, WM_CLASS, STRING_NO_CC, &ss);
if (got) {
if (ss[0]) {
if (self->class == NULL) self->class = g_strdup("");
/* get the WM_WINDOW_ROLE. make it "" if it is not provided */
- got = FALSE;
- if (leader)
- got = OBT_PROP_GETS(leader, WM_WINDOW_ROLE, locale, &s);
- if (!got)
- got = OBT_PROP_GETS(self->window, WM_WINDOW_ROLE, locale, &s);
+ got = OBT_PROP_GETS_XPCS(self->window, WM_WINDOW_ROLE, &s);
if (got)
self->role = s;
got = FALSE;
if (leader)
- got = OBT_PROP_GETSS(leader, WM_COMMAND, locale, &ss);
+ got = OBT_PROP_GETSS(leader, WM_COMMAND, &ss);
if (!got)
- got = OBT_PROP_GETSS(self->window, WM_COMMAND, locale, &ss);
+ got = OBT_PROP_GETSS(self->window, WM_COMMAND, &ss);
if (got) {
/* merge/mash them all together */
/* get the WM_CLIENT_MACHINE */
got = FALSE;
if (leader)
- got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, locale, &s);
+ got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, &s);
if (!got)
- got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, locale, &s);
+ got = OBT_PROP_GETS(self->window, WM_CLIENT_MACHINE, &s);
if (got) {
gchar localhost[128];
}
}
-/*! Save the session IDs 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_session_ids(ObClient *self)
+/*! 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)
{
- OBT_PROP_SETS(self->window, OB_ROLE, utf8, self->role);
- OBT_PROP_SETS(self->window, OB_NAME, utf8, self->name);
- OBT_PROP_SETS(self->window, OB_CLASS, utf8, self->class);
+ const gchar *type;
+
+ OBT_PROP_SETS(self->window, OB_APP_ROLE, self->role);
+ OBT_PROP_SETS(self->window, OB_APP_NAME, self->name);
+ OBT_PROP_SETS(self->window, OB_APP_CLASS, self->class);
+ OBT_PROP_SETS(self->window, OB_APP_TITLE, 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, type);
}
static void client_change_wm_state(ObClient *self)
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;
}
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
/* 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 */
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) ?
*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;
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 its premax area instead. */
if (self->max_horz) {
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);
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;
}
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
/* 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;
}
-static gboolean client_validate_unmap(ObClient *self, int n)
-{
- XEvent e;
- gboolean ret = TRUE;
-
- if (XCheckTypedWindowEvent(obt_display, self->window, UnmapNotify, &e)) {
- if (n < self->ignore_unmaps) // ignore this one, but look for more
- ret = client_validate_unmap(self, n+1);
- else
- ret = FALSE; // the window is going to become unmanaged
+struct ObClientFindDestroyUnmap {
+ Window window;
+ gint ignore_unmaps;
+};
- /* put them back on the event stack so they end up in the same order */
- XPutBackEvent(obt_display, &e);
- }
-
- return ret;
+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)) {
- XPutBackEvent(obt_display, &e);
- return FALSE;
- }
-
- if (!client_validate_unmap(self, 0))
+ find.window = self->window;
+ find.ignore_unmaps = self->ignore_unmaps;
+ if (xqueue_exists_local(find_destroy_unmap, &find))
return FALSE;
return TRUE;
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 */
/* 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;
if ((user && (desktop ||
self->desktop == DESKTOP_ALL ||
self->desktop == screen_desktop)) ||
- client_can_steal_focus(self, event_curtime, CurrentTime))
+ client_can_steal_focus(self, desktop, event_time(), CurrentTime))
{
client_present(self, here, raise, unshade);
}
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_free(area);
+ g_slice_free(Rect, area);
}
/* search for edges of clients */
- for (it = client_list; it; it = g_list_next(it)) {
- ObClient *cur = it->data;
+ 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;
- /* 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;
+ /* 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;
- ob_debug("trying window %s", cur->title);
+ ob_debug("trying window %s", cur->title);
- detect_edge(cur->frame->area, dir, my_head, my_size, my_edge_start,
+ 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_slice_free(Rect, a);
}
void client_find_move_directional(ObClient *self, ObDirection dir,