-#include "debug.h"
+/* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
+
+ client.c for the Openbox window manager
+ Copyright (c) 2003 Ben Jansens
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ See the COPYING file for a copy of the GNU General Public License.
+*/
+
#include "client.h"
+#include "debug.h"
+#include "startupnotify.h"
#include "dock.h"
#include "xerror.h"
-#include "startup.h"
#include "screen.h"
#include "moveresize.h"
+#include "place.h"
#include "prop.h"
#include "extensions.h"
#include "frame.h"
+#include "session.h"
#include "event.h"
#include "grab.h"
#include "focus.h"
#include "stacking.h"
-#include "dispatch.h"
#include "openbox.h"
#include "group.h"
#include "config.h"
-#include "menu.h"
+#include "menuframe.h"
+#include "keyboard.h"
+#include "mouse.h"
#include "render/render.h"
#include <glib.h>
#define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
ButtonMotionMask)
-GList *client_list = NULL;
+GList *client_list = NULL;
+GSList *client_destructors = NULL;
static void client_get_all(ObClient *self);
static void client_toggle_border(ObClient *self, gboolean show);
+static void client_get_startup_id(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_change_allowed_actions(ObClient *self);
static void client_change_state(ObClient *self);
static void client_apply_startup_state(ObClient *self);
+static void client_restore_session_state(ObClient *self);
+static void client_restore_session_stacking(ObClient *self);
+static void client_urgent_notify(ObClient *self);
-void client_startup()
+void client_startup(gboolean reconfig)
{
+ if (reconfig) return;
+
client_set_list();
}
-void client_shutdown()
+void client_shutdown(gboolean reconfig)
{
}
+void client_add_destructor(GDestroyNotify func)
+{
+ client_destructors = g_slist_prepend(client_destructors, (gpointer)func);
+}
+
+void client_remove_destructor(GDestroyNotify func)
+{
+ client_destructors = g_slist_remove(client_destructors, (gpointer)func);
+}
+
void client_set_list()
{
Window *windows, *win_it;
}
XFree(children);
- /* stack them as they were on startup!
- why with stacking_lower? Why, because then windows who aren't in the
- stacking list are on the top where you can see them instead of buried
- at the bottom! */
- for (i = startup_stack_size; i > 0; --i) {
- ObWindow *obw;
-
- w = startup_stack_order[i-1];
- obw = g_hash_table_lookup(window_map, &w);
- if (obw) {
- g_assert(WINDOW_IS_CLIENT(obw));
- stacking_lower(CLIENT_AS_WINDOW(obw));
- }
- }
- g_free(startup_stack_order);
- startup_stack_order = NULL;
- startup_stack_size = 0;
-
- if (config_focus_new) {
- ObWindow *active;
-
- active = g_hash_table_lookup(window_map, &startup_active);
- if (active) {
- g_assert(WINDOW_IS_CLIENT(active));
- if (!client_focus(WINDOW_AS_CLIENT(active)))
- focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
- } else
- focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
- }
+ if (config_focus_new)
+ focus_fallback(OB_FOCUS_FALLBACK_NOFOCUS);
}
void client_manage(Window window)
/* create the ObClient struct, and populate it from the hints on the
window */
- self = g_new(ObClient, 1);
+ self = g_new0(ObClient, 1);
self->obwin.type = Window_Client;
self->window = window;
+
+ /* non-zero defaults */
+ self->title_count = 1;
+ self->wmstate = NormalState;
+ self->layer = -1;
+ self->decorate = TRUE;
+ self->desktop = screen_num_desktops; /* always an invalid value */
+
client_get_all(self);
+ client_restore_session_state(self);
+
+ sn_app_started(self->class);
+
+ client_change_state(self);
/* remove the client's border (and adjust re gravity) */
client_toggle_border(self, FALSE);
frame_grab_client(self->frame, self);
- client_apply_startup_state(self);
-
grab_server(FALSE);
- /* add to client list/map */
- client_list = g_list_append(client_list, self);
- g_hash_table_insert(window_map, &self->window, self);
+ client_apply_startup_state(self);
/* update the focus lists */
focus_order_add_new(self);
stacking_add(CLIENT_AS_WINDOW(self));
+ client_restore_session_stacking(self);
/* focus the new window? */
if (ob_state() != OB_STATE_STARTING && config_focus_new &&
(self->type == OB_CLIENT_TYPE_NORMAL ||
self->type == OB_CLIENT_TYPE_DIALOG))
{
+ activate = TRUE;
+#if 0
if (self->desktop != screen_desktop) {
/* activate the window */
activate = TRUE;
activate = TRUE;
}
}
+#endif
}
- dispatch_client(Event_Client_New, self, 0, 0);
+ if (ob_state() == OB_STATE_RUNNING) {
+ int x = self->area.x, ox = x;
+ int y = self->area.y, oy = y;
- /* make sure the window is visible */
- client_move_onscreen(self);
+ place_client(self, &x, &y);
- screen_update_areas();
+ /* make sure the window is visible */
+ client_find_onscreen(self, &x, &y,
+ self->frame->area.width,
+ self->frame->area.height,
+ client_normal(self));
+
+ if (x != ox || y != oy)
+ client_move(self, x, y);
+ }
client_showhide(self);
if (screen_showing_desktop)
screen_show_desktop(FALSE);
+ /* add to client list/map */
+ client_list = g_list_append(client_list, self);
+ g_hash_table_insert(window_map, &self->window, self);
+
+ /* this has to happen after we're in the client_list */
+ screen_update_areas();
+
/* update the list hints */
client_set_list();
- dispatch_client(Event_Client_Mapped, self, 0, 0);
+ keyboard_grab_for_client(self, TRUE);
+ mouse_grab_for_client(self, TRUE);
ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
}
client_unmanage(client_list->data);
}
-/* called by client_unmanage() to close any menus referencing this client */
-void client_close_menus(gpointer key, gpointer value, gpointer self)
-{
- if (((ObMenu *)value)->client == (ObClient *)self)
- menu_hide((ObMenu *)value);
-}
-
void client_unmanage(ObClient *self)
{
guint j;
ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class);
- dispatch_client(Event_Client_Destroy, self, 0, 0);
g_assert(self != NULL);
+ keyboard_grab_for_client(self, FALSE);
+ mouse_grab_for_client(self, FALSE);
+
/* remove the window from our save set */
XChangeSaveSet(ob_display, self->window, SetModeDelete);
}
}
- if (moveresize_client == self)
- moveresize_end(TRUE);
-
- /* close any windows that are attached to this window */
- g_hash_table_foreach(menu_hash, client_close_menus, self);
-
-
+ for (it = client_destructors; it; it = g_slist_next(it)) {
+ GDestroyNotify func = (GDestroyNotify) it->data;
+ func(self);
+ }
+
if (focus_client == self) {
XEvent e;
self->group = NULL;
}
- /* dispatch the unmapped event */
- dispatch_client(Event_Client_Unmapped, self, 0, 0);
- g_assert(self != NULL);
-
/* give the client its border back */
client_toggle_border(self, TRUE);
unmapping/mapping */
PROP_ERASE(self->window, net_wm_desktop);
PROP_ERASE(self->window, net_wm_state);
+ PROP_ERASE(self->window, wm_state);
} else {
/* if we're left in an iconic state, the client wont be mapped. this is
bad, since we will no longer be managing the window on restart */
g_free(self->name);
g_free(self->class);
g_free(self->role);
+ g_free(self->sm_client_id);
g_free(self);
/* update the list hints */
client_set_list();
}
-void client_move_onscreen(ObClient *self)
+static void client_urgent_notify(ObClient *self)
+{
+ if (self->urgent)
+ frame_flash_start(self->frame);
+ else
+ frame_flash_stop(self->frame);
+}
+
+static void client_restore_session_state(ObClient *self)
+{
+ GList *it;
+
+ if (!(it = session_state_find(self)))
+ return;
+
+ self->session = it->data;
+
+ RECT_SET(self->area, self->session->x, self->session->y,
+ self->session->w, self->session->h);
+ self->positioned = TRUE;
+ XResizeWindow(ob_display, self->window,
+ self->session->w, self->session->h);
+
+ self->desktop = (self->session->desktop == DESKTOP_ALL ?
+ self->session->desktop :
+ MIN(screen_num_desktops - 1, self->session->desktop));
+ PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
+
+ self->shaded = self->session->shaded;
+ self->iconic = self->session->iconic;
+ self->skip_pager = self->session->skip_pager;
+ self->skip_taskbar = self->session->skip_taskbar;
+ self->fullscreen = self->session->fullscreen;
+ self->above = self->session->above;
+ self->below = self->session->below;
+ self->max_horz = self->session->max_horz;
+ self->max_vert = self->session->max_vert;
+}
+
+static void client_restore_session_stacking(ObClient *self)
+{
+ GList *it;
+
+ if (!self->session) return;
+
+ it = g_list_find(session_saved_state, self->session);
+ for (it = g_list_previous(it); it; it = g_list_previous(it)) {
+ GList *cit;
+
+ for (cit = client_list; cit; cit = g_list_next(cit))
+ if (session_state_cmp(it->data, cit->data))
+ break;
+ if (cit) {
+ client_calc_layer(self);
+ stacking_below(CLIENT_AS_WINDOW(self),
+ CLIENT_AS_WINDOW(cit->data));
+ break;
+ }
+ }
+}
+
+void client_move_onscreen(ObClient *self, gboolean rude)
+{
+ int x = self->area.x;
+ int y = self->area.y;
+ if (client_find_onscreen(self, &x, &y,
+ self->frame->area.width,
+ self->frame->area.height, rude)) {
+ client_move(self, x, y);
+ }
+}
+
+gboolean client_find_onscreen(ObClient *self, int *x, int *y, int w, int h,
+ gboolean rude)
{
Rect *a;
- int x = self->frame->area.x, y = self->frame->area.y;
+ int ox = *x, oy = *y;
+
+ frame_client_gravity(self->frame, x, y); /* get where the frame
+ would be */
/* XXX watch for xinerama dead areas */
+
a = screen_area(self->desktop);
- if (x >= a->x + a->width - 1)
- x = a->x + a->width - self->frame->area.width;
- if (y >= a->y + a->height - 1)
- y = a->y + a->height - self->frame->area.height;
- if (x + self->frame->area.width - 1 < a->x)
- x = a->x;
- if (y + self->frame->area.height - 1< a->y)
- y = a->y;
-
- frame_frame_gravity(self->frame, &x, &y); /* get where the client
- should be */
- client_configure(self, OB_CORNER_TOPLEFT, x, y,
- self->area.width, self->area.height,
- TRUE, TRUE);
+ if (client_normal(self)) {
+ if (!self->strut.right && *x >= a->x + a->width - 1)
+ *x = a->x + a->width - self->frame->area.width;
+ if (!self->strut.bottom && *y >= a->y + a->height - 1)
+ *y = a->y + a->height - self->frame->area.height;
+ if (!self->strut.left && *x + self->frame->area.width - 1 < a->x)
+ *x = a->x;
+ if (!self->strut.top && *y + self->frame->area.height - 1 < a->y)
+ *y = a->y;
+ }
+
+ if (rude) {
+ /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
+ Java can suck it too. */
+
+ /* dont let windows map/move into the strut unless they
+ are bigger than the available area */
+ if (w <= a->width) {
+ if (!self->strut.left && *x < a->x) *x = a->x;
+ if (!self->strut.right && *x + w > a->x + a->width)
+ *x = a->x + a->width - w;
+ }
+ if (h <= a->height) {
+ if (!self->strut.top && *y < a->y) *y = a->y;
+ if (!self->strut.bottom && *y + h > a->y + a->height)
+ *y = a->y + a->height - h;
+ }
+ }
+
+ frame_frame_gravity(self->frame, x, y); /* get where the client
+ should be */
+
+ return ox != *x || oy != *y;
}
static void client_toggle_border(ObClient *self, gboolean show)
static void client_get_all(ObClient *self)
{
- /* update EVERYTHING!! */
-
- self->ignore_unmaps = 0;
-
- /* defaults */
- self->frame = NULL;
- self->title = self->icon_title = NULL;
- self->title_count = 1;
- self->name = self->class = self->role = NULL;
- self->wmstate = NormalState;
- self->transient = FALSE;
- self->transients = NULL;
- self->transient_for = NULL;
- self->layer = -1;
- self->urgent = FALSE;
- self->positioned = FALSE;
- self->decorate = TRUE;
- self->group = NULL;
- self->nicons = 0;
-
client_get_area(self);
client_update_transient_for(self);
client_update_wmhints(self);
+ client_get_startup_id(self);
client_get_desktop(self);
client_get_state(self);
client_get_shaped(self);
client_update_title(self);
client_update_class(self);
+ client_update_sm_client_id(self);
client_update_strut(self);
client_update_icons(self);
+}
- client_change_state(self);
+static void client_get_startup_id(ObClient *self)
+{
+ if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
+ if (self->group)
+ PROP_GETS(self->group->leader,
+ net_startup_id, utf8, &self->startup_id);
}
static void client_get_area(ObClient *self)
}
}
}
- if (!trdesk)
- /* defaults to the current desktop */
- self->desktop = screen_desktop;
-
+ if (!trdesk) {
+ /* try get from the startup-notification protocol */
+ if (sn_get_desktop(self->startup_id, &self->desktop)) {
+ if (self->desktop >= screen_num_desktops &&
+ self->desktop != DESKTOP_ALL)
+ self->desktop = screen_num_desktops - 1;
+ } else
+ /* defaults to the current desktop */
+ self->desktop = screen_desktop;
+ }
}
if (self->desktop != d) {
/* set the desktop hint, to make sure that it always exists */
guint32 *state;
guint num;
- self->modal = self->shaded = self->max_horz = self->max_vert =
- self->fullscreen = self->above = self->below = self->iconic =
- self->skip_taskbar = self->skip_pager = FALSE;
-
if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
gulong i;
for (i = 0; i < num; ++i) {
target = g_hash_table_lookup(window_map, &t);
/* if this happens then we need to check for it*/
g_assert(target != self);
- g_assert(!target || WINDOW_IS_CLIENT(target));
+ if (target && !WINDOW_IS_CLIENT(target)) {
+ /* this can happen when a dialog is a child of
+ a dockapp, for example */
+ target = NULL;
+ }
if (!target && self->group) {
/* not transient to a client, see if it is transient for a
/* get the hints from the window */
if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
- if (size.flags & (PPosition|USPosition)) {
- Rect *a;
-
- /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
- Java can suck it too. */
-
- /* dont let windows map above/left into the strut unless they
- are bigger than the available area */
- a = screen_area(self->desktop);
- if (self->area.width <= a->width && self->area.x < a->x)
- self->area.x = a->x;
- if (self->area.height <= a->height && self->area.y < a->y)
- self->area.y = a->y;
-
- self->positioned = TRUE;
- }
+ self->positioned = !!(size.flags & (PPosition|USPosition));
if (size.flags & PWinGravity) {
self->gravity = size.win_gravity;
void client_setup_decor_and_functions(ObClient *self)
{
/* start with everything (cept fullscreen) */
- self->decorations = (OB_FRAME_DECOR_TITLEBAR |
- OB_FRAME_DECOR_HANDLE |
- OB_FRAME_DECOR_GRIPS |
- OB_FRAME_DECOR_BORDER |
- OB_FRAME_DECOR_ICON |
- OB_FRAME_DECOR_ALLDESKTOPS |
- OB_FRAME_DECOR_ICONIFY |
- OB_FRAME_DECOR_MAXIMIZE |
- OB_FRAME_DECOR_SHADE);
- self->functions = (OB_CLIENT_FUNC_RESIZE |
- OB_CLIENT_FUNC_MOVE |
- OB_CLIENT_FUNC_ICONIFY |
- OB_CLIENT_FUNC_MAXIMIZE |
- OB_CLIENT_FUNC_SHADE);
+ self->decorations =
+ (OB_FRAME_DECOR_TITLEBAR |
+ (ob_rr_theme->show_handle ? OB_FRAME_DECOR_HANDLE : 0) |
+ OB_FRAME_DECOR_GRIPS |
+ OB_FRAME_DECOR_BORDER |
+ OB_FRAME_DECOR_ICON |
+ OB_FRAME_DECOR_ALLDESKTOPS |
+ OB_FRAME_DECOR_ICONIFY |
+ OB_FRAME_DECOR_MAXIMIZE |
+ OB_FRAME_DECOR_SHADE);
+ self->functions =
+ (OB_CLIENT_FUNC_RESIZE |
+ OB_CLIENT_FUNC_MOVE |
+ OB_CLIENT_FUNC_ICONIFY |
+ OB_CLIENT_FUNC_MAXIMIZE |
+ OB_CLIENT_FUNC_SHADE);
if (self->delete_window) {
self->functions |= OB_CLIENT_FUNC_CLOSE;
self->decorations |= OB_FRAME_DECOR_CLOSE;
self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
}
+ /* kill the handle on fully maxed windows */
+ if (self->max_vert && self->max_horz)
+ self->decorations &= ~OB_FRAME_DECOR_HANDLE;
+
/* finally, the user can have requested no decorations, which overrides
everything */
if (!self->decorate)
- self->decorations = 0;
+ self->decorations = OB_FRAME_DECOR_BORDER;
/* if we don't have a titlebar, then we cannot shade! */
if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
client_change_allowed_actions(self);
if (self->frame) {
- /* this makes sure that these windows appear on all desktops */
- if (self->type == OB_CLIENT_TYPE_DESKTOP &&
- self->desktop != DESKTOP_ALL)
- client_set_desktop(self, DESKTOP_ALL, FALSE);
-
- /* adjust the client's decorations, etc. */
- client_reconfigure(self);
+ /* adjust the client's decorations, etc. */
+ client_reconfigure(self);
} else {
/* this makes sure that these windows appear on all desktops */
if (self->type == OB_CLIENT_TYPE_DESKTOP &&
}
}
- /* the WM_HINTS can contain an icon */
- client_update_icons(self);
-
/* because the self->transient flag wont change from this call,
we don't need to update the window's type and such, only its
transient_for, and the transients lists of other windows in
client_update_transient_for(self);
}
+ /* the WM_HINTS can contain an icon */
+ client_update_icons(self);
+
XFree(hints);
}
/* fire the urgent callback if we're mapped, otherwise, wait until
after we're mapped */
if (self->frame)
- dispatch_client(Event_Client_Urgent, self, self->urgent, 0);
+ client_urgent_notify(self);
}
}
GList *it;
guint32 nums;
guint i;
- char *data = NULL;
+ gchar *data = NULL;
gboolean read_title;
+ gchar *old_title;
- g_free(self->title);
+ old_title = self->title;
/* try netwm */
if (!PROP_GETS(self->window, net_wm_name, utf8, &data))
if (!PROP_GETS(self->window, wm_name, locale, &data))
data = g_strdup("Unnamed Window");
+ /* did the title change? then reset the title_count */
+ if (old_title && 0 != strncmp(old_title, data, strlen(data)))
+ self->title_count = 1;
+
/* look for duplicates and append a number */
nums = 0;
for (it = client_list; it; it = it->next)
}
/* dont display the number for the first window */
if (self->title_count > 1) {
- char *vdata, *ndata;
- ndata = g_strdup_printf(" - [%u]", self->title_count);
- vdata = g_strconcat(data, ndata, NULL);
- g_free(ndata);
+ char *ndata;
+ ndata = g_strdup_printf("%s - [%u]", data, self->title_count);
g_free(data);
- data = vdata;
+ data = ndata;
}
PROP_SETS(self->window, net_wm_visible_name, data);
if (self->frame)
frame_adjust_title(self->frame);
+ g_free(old_title);
+
/* update the icon title */
data = NULL;
g_free(self->icon_title);
}
if (PROP_GETS(self->window, wm_window_role, locale, &s))
- self->role = g_strdup(s);
+ self->role = s;
if (self->name == NULL) self->name = g_strdup("");
if (self->class == NULL) self->class = g_strdup("");
{
guint num;
guint32 *data;
+ gboolean got = FALSE;
+ StrutPartial strut;
+
+ if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
+ &data, &num)) {
+ if (num == 12) {
+ got = TRUE;
+ STRUT_PARTIAL_SET(strut,
+ data[0], data[2], data[1], data[3],
+ data[4], data[5], data[8], data[9],
+ data[6], data[7], data[10], data[11]);
+ }
+ g_free(data);
+ }
- if (!PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
- STRUT_SET(self->strut, 0, 0, 0, 0);
- } else {
- if (num == 4)
- STRUT_SET(self->strut, data[0], data[2], data[1], data[3]);
- else
- STRUT_SET(self->strut, 0, 0, 0, 0);
- g_free(data);
+ if (!got &&
+ PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
+ if (num == 4) {
+ got = TRUE;
+ STRUT_PARTIAL_SET(strut,
+ data[0], data[2], data[1], data[3],
+ 0, 0, 0, 0, 0, 0, 0, 0);
+ }
+ g_free(data);
}
- /* updating here is pointless while we're being mapped cuz we're not in
- the client list yet */
- if (self->frame)
- screen_update_areas();
+ if (!got)
+ STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0);
+
+ if (!STRUT_EQUAL(strut, self->strut)) {
+ self->strut = strut;
+
+ /* updating here is pointless while we're being mapped cuz we're not in
+ the client list yet */
+ if (self->frame)
+ screen_update_areas();
+ }
}
void client_update_icons(ObClient *self)
}
}
+ if (!self->nicons) {
+ self->nicons++;
+ self->icons = g_new(ObClientIcon, self->nicons);
+ self->icons[self->nicons-1].width = 48;
+ self->icons[self->nicons-1].height = 48;
+ self->icons[self->nicons-1].data = g_memdup(ob_rr_theme->def_win_icon,
+ sizeof(RrPixel32)
+ * 48 * 48);
+ }
+
if (self->frame)
frame_adjust_icon(self->frame);
}
client_shade(self, TRUE);
}
if (self->urgent)
- dispatch_client(Event_Client_Urgent, self, self->urgent, 0);
+ client_urgent_notify(self);
if (self->max_vert && self->max_horz) {
self->max_vert = self->max_horz = FALSE;
*/
}
-void client_configure(ObClient *self, ObCorner anchor,
- int x, int y, int w, int h,
- gboolean user, gboolean final)
+void client_configure_full(ObClient *self, ObCorner anchor,
+ int x, int y, int w, int h,
+ gboolean user, gboolean final,
+ gboolean force_reply)
{
+ gint oldw, oldh;
+ gboolean send_resize_client;
gboolean moved = FALSE, resized = FALSE;
+ guint fdecor = self->frame->decorations;
+ gboolean fhorz = self->frame->max_horz;
+
+ /* make the frame recalculate its dimentions 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, TRUE, TRUE, TRUE);
/* gets the frame's position */
frame_client_gravity(self->frame, &x, &y);
/* set the size and position if maximized */
if (self->max_horz) {
- x = a->x - self->frame->size.left;
- w = a->width;
+ x = a->x;
+ w = a->width - self->frame->size.left - self->frame->size.right;
}
if (self->max_vert) {
y = a->y;
minh = self->base_size.height;
}
- if (user) {
- /* for interactive resizing. have to move half an increment in each
- direction. */
-
- /* how far we are towards the next size inc */
- int mw = (w - basew) % self->size_inc.width;
- int mh = (h - baseh) % self->size_inc.height;
- /* amount to add */
- int aw = self->size_inc.width / 2;
- int ah = self->size_inc.height / 2;
- /* don't let us move into a new size increment */
- if (mw + aw >= self->size_inc.width)
- aw = self->size_inc.width - mw - 1;
- if (mh + ah >= self->size_inc.height)
- ah = self->size_inc.height - mh - 1;
- w += aw;
- h += ah;
-
- /* if this is a user-requested resize, then check against min/max
- sizes */
-
- /* smaller than min size or bigger than max size? */
- if (w > self->max_size.width) w = self->max_size.width;
- if (w < minw) w = minw;
- if (h > self->max_size.height) h = self->max_size.height;
- if (h < minh) h = minh;
- }
+ /* if this is a user-requested resize, then check against min/max
+ sizes */
+
+ /* smaller than min size or bigger than max size? */
+ if (w > self->max_size.width) w = self->max_size.width;
+ if (w < minw) w = minw;
+ if (h > self->max_size.height) h = self->max_size.height;
+ if (h < minh) h = minh;
w -= basew;
h -= baseh;
h /= self->size_inc.height;
/* you cannot resize to nothing */
- if (w < 1) w = 1;
- if (h < 1) h = 1;
+ if (basew + w < 1) w = 1 - basew;
+ if (baseh + h < 1) h = 1 - baseh;
/* store the logical size */
- SIZE_SET(self->logical_size, w, h);
+ SIZE_SET(self->logical_size,
+ self->size_inc.width > 1 ? w : w + basew,
+ self->size_inc.height > 1 ? h : h + baseh);
w *= self->size_inc.width;
h *= self->size_inc.height;
w += basew;
h += baseh;
- if (user) {
- /* adjust the height to match the width for the aspect ratios.
- for this, min size is not substituted for base size ever. */
- w -= self->base_size.width;
- h -= self->base_size.height;
+ /* adjust the height to match the width for the aspect ratios.
+ for this, min size is not substituted for base size ever. */
+ w -= self->base_size.width;
+ h -= self->base_size.height;
- if (self->min_ratio)
- if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
- if (self->max_ratio)
- if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
+ if (self->min_ratio)
+ if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
+ if (self->max_ratio)
+ if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
- w += self->base_size.width;
- h += self->base_size.height;
- }
+ w += self->base_size.width;
+ h += self->base_size.height;
}
switch (anchor) {
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;
RECT_SET(self->area, x, y, w, h);
/* for app-requested resizes, always resize if 'resized' is true.
for user-requested ones, only resize if final is true, or when
- resizing in opaque mode */
- if ((!user && resized) ||
- (user && (final || (resized && config_opaque_resize))))
- XResizeWindow(ob_display, self->window, w, h);
+ resizing in redraw mode */
+ send_resize_client = ((!user && resized) ||
+ (user && (final ||
+ (resized && config_redraw_resize))));
+
+ /* if the client is enlarging, the resize the client before the frame */
+ if (send_resize_client && user && (w > oldw || h > oldh))
+ XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
/* move/resize the frame to match the request */
if (self->frame) {
- if (self->decorations != self->frame->decorations)
+ if (self->decorations != fdecor || self->max_horz != fhorz)
moved = resized = TRUE;
if (moved || resized)
- frame_adjust_area(self->frame, moved, resized);
+ frame_adjust_area(self->frame, moved, resized, FALSE);
- /* If you send this and the client hasn't changed you end up with buggy
- clients (emacs) freaking out, cuz they send back a configure every
- time they receive this event, which resends them this event... etc.
- */
- if ((!user && moved) || (user && final)) {
+ if (!resized && (force_reply || ((!user && moved) || (user && final))))
+ {
XEvent event;
event.type = ConfigureNotify;
event.xconfigure.display = ob_display;
event.xconfigure.event = self->window;
event.xconfigure.window = self->window;
-
+
/* root window real coords */
- event.xconfigure.x = self->frame->area.x + self->frame->size.left;
- event.xconfigure.y = self->frame->area.y + self->frame->size.top;
-
+ event.xconfigure.x = self->frame->area.x + self->frame->size.left -
+ self->border_width;
+ event.xconfigure.y = self->frame->area.y + self->frame->size.top -
+ self->border_width;
event.xconfigure.width = w;
event.xconfigure.height = h;
event.xconfigure.border_width = 0;
event.xconfigure.override_redirect = FALSE;
XSendEvent(event.xconfigure.display, event.xconfigure.window,
FALSE, StructureNotifyMask, &event);
- }
+ }
}
+
+ /* if the client is shrinking, then resize the frame before the client */
+ if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
+ XResizeWindow(ob_display, self->window, w, h);
+
+ XFlush(ob_display);
}
void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
client_setup_decor_and_functions(self);
- client_configure(self, OB_CORNER_TOPLEFT, x, y, w, h, TRUE, TRUE);
+ client_move_resize(self, x, y, w, h);
/* try focus us when we go into fullscreen mode */
client_focus(self);
client_change_state(self);
client_showhide(self);
screen_update_areas();
-
- dispatch_client(iconic ? Event_Client_Unmapped : Event_Client_Mapped,
- self, 0, 0);
}
/* iconify all transients */
client_change_state(self); /* change the state hints on the client */
+ client_setup_decor_and_functions(self);
+
/* figure out where the client should be going */
frame_frame_gravity(self->frame, &x, &y);
- client_configure(self, OB_CORNER_TOPLEFT, x, y, w, h, TRUE, TRUE);
+ client_move_resize(self, x, y, w, h);
}
void client_shade(ObClient *self, gboolean shade)
self->shaded = shade;
client_change_state(self);
/* resize the frame to just the titlebar */
- frame_adjust_area(self->frame, FALSE, FALSE);
+ frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
}
void client_close(ObClient *self)
focus_order_to_top(self);
else
focus_order_to_bottom(self);
-
- dispatch_client(Event_Client_Desktop, self, target, old);
}
/* move all transients */
return FALSE;
}
- if (self->can_focus)
+ if (self->can_focus) {
/* RevertToPointerRoot causes much more headache than RevertToNone, so
I choose to use it always, hopefully to find errors quicker, if any
- are left. (I hate X. I hate focus events.) */
- XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
+ are left. (I hate X. I hate focus events.)
+
+ Update: Changing this to RevertToNone fixed a bug with mozilla (bug
+ #799. So now it is RevertToNone again.
+ */
+ XSetInputFocus(ob_display, self->window, RevertToNone,
event_lasttime);
+ }
if (self->focus_notify) {
XEvent ce;
focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
}
-void client_activate(ObClient *self)
+void client_activate(ObClient *self, gboolean here)
{
if (client_normal(self) && screen_showing_desktop)
screen_show_desktop(FALSE);
if (self->iconic)
client_iconify(self, FALSE, FALSE);
if (self->desktop != DESKTOP_ALL &&
- self->desktop != screen_desktop)
- screen_set_desktop(self->desktop);
- else if (!self->frame->visible)
+ self->desktop != screen_desktop) {
+ if (here)
+ client_set_desktop(self, screen_desktop, FALSE);
+ else
+ screen_set_desktop(self->desktop);
+ } else if (!self->frame->visible)
/* if its not visible for other reasons, then don't mess
with it */
return;
/* li is the largest image < req */
unsigned long size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
- if (!self->nicons) return NULL;
-
for (i = 0; i < self->nicons; ++i) {
size = self->icons[i].width * self->icons[i].height;
if (size < smallest && size >= (unsigned)(w * h)) {
his_cy = (cur->frame->area.y - my_cy)
+ cur->frame->area.height / 2;
- if(dir > 3) {
+ if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
+ dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
int tx;
/* Rotate the diagonals 45 degrees counterclockwise.
* To do this, multiply the matrix /+h +h\ with the
}
return NULL;
}
+
+void client_update_sm_client_id(ObClient *self)
+{
+ g_free(self->sm_client_id);
+ self->sm_client_id = NULL;
+
+ if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
+ self->group)
+ PROP_GETS(self->group->leader, sm_client_id, locale,
+ &self->sm_client_id);
+}
+
+/* finds the nearest edge in the given direction from the current client
+ * note to self: the edge is the -frame- edge (the actual one), not the
+ * client edge.
+ */
+int client_directional_edge_search(ObClient *c, ObDirection dir)
+{
+ int dest;
+ int my_edge_start, my_edge_end, my_offset;
+ GList *it;
+ Rect *a;
+
+ if(!client_list)
+ return -1;
+
+ a = screen_area(c->desktop);
+
+ switch(dir) {
+ case OB_DIRECTION_NORTH:
+ my_edge_start = c->frame->area.x;
+ my_edge_end = c->frame->area.x + c->frame->area.width;
+ my_offset = c->frame->area.y;
+
+ dest = a->y; /* default: top of screen */
+
+ for(it = g_list_first(client_list); it; it = it->next) {
+ int his_edge_start, his_edge_end, his_offset;
+ ObClient *cur = it->data;
+
+ if(cur == c)
+ continue;
+ if(!client_normal(cur))
+ continue;
+ if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
+ continue;
+ if(cur->iconic)
+ continue;
+
+ his_edge_start = cur->frame->area.x;
+ his_edge_end = cur->frame->area.x + cur->frame->area.width;
+ his_offset = cur->frame->area.y + cur->frame->area.height;
+
+ if(his_offset + 1 > my_offset)
+ continue;
+
+ if(his_offset < dest)
+ continue;
+
+ if(his_edge_start >= my_edge_start &&
+ his_edge_start <= my_edge_end)
+ dest = his_offset;
+
+ if(my_edge_start >= his_edge_start &&
+ my_edge_start <= his_edge_end)
+ dest = his_offset;
+
+ }
+ break;
+ case OB_DIRECTION_SOUTH:
+ my_edge_start = c->frame->area.x;
+ my_edge_end = c->frame->area.x + c->frame->area.width;
+ my_offset = c->frame->area.y + c->frame->area.height;
+
+ dest = a->y + a->height; /* default: bottom of screen */
+
+ for(it = g_list_first(client_list); it; it = it->next) {
+ int his_edge_start, his_edge_end, his_offset;
+ ObClient *cur = it->data;
+
+ if(cur == c)
+ continue;
+ if(!client_normal(cur))
+ continue;
+ if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
+ continue;
+ if(cur->iconic)
+ continue;
+
+ his_edge_start = cur->frame->area.x;
+ his_edge_end = cur->frame->area.x + cur->frame->area.width;
+ his_offset = cur->frame->area.y;
+
+
+ if(his_offset - 1 < my_offset)
+ continue;
+
+ if(his_offset > dest)
+ continue;
+
+ if(his_edge_start >= my_edge_start &&
+ his_edge_start <= my_edge_end)
+ dest = his_offset;
+
+ if(my_edge_start >= his_edge_start &&
+ my_edge_start <= his_edge_end)
+ dest = his_offset;
+
+ }
+ break;
+ case OB_DIRECTION_WEST:
+ my_edge_start = c->frame->area.y;
+ my_edge_end = c->frame->area.y + c->frame->area.height;
+ my_offset = c->frame->area.x;
+
+ dest = a->x; /* default: leftmost egde of screen */
+
+ for(it = g_list_first(client_list); it; it = it->next) {
+ int his_edge_start, his_edge_end, his_offset;
+ ObClient *cur = it->data;
+
+ if(cur == c)
+ continue;
+ if(!client_normal(cur))
+ continue;
+ if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
+ continue;
+ if(cur->iconic)
+ continue;
+
+ his_edge_start = cur->frame->area.y;
+ his_edge_end = cur->frame->area.y + cur->frame->area.height;
+ his_offset = cur->frame->area.x + cur->frame->area.width;
+
+ if(his_offset + 1 > my_offset)
+ continue;
+
+ if(his_offset < dest)
+ continue;
+
+ if(his_edge_start >= my_edge_start &&
+ his_edge_start <= my_edge_end)
+ dest = his_offset;
+
+ if(my_edge_start >= his_edge_start &&
+ my_edge_start <= his_edge_end)
+ dest = his_offset;
+
+
+ }
+ break;
+ case OB_DIRECTION_EAST:
+ my_edge_start = c->frame->area.y;
+ my_edge_end = c->frame->area.y + c->frame->area.height;
+ my_offset = c->frame->area.x + c->frame->area.width;
+
+ dest = a->x + a->width; /* default: rightmost edge of screen */
+
+ for(it = g_list_first(client_list); it; it = it->next) {
+ int his_edge_start, his_edge_end, his_offset;
+ ObClient *cur = it->data;
+
+ if(cur == c)
+ continue;
+ if(!client_normal(cur))
+ continue;
+ if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
+ continue;
+ if(cur->iconic)
+ continue;
+
+ his_edge_start = cur->frame->area.y;
+ his_edge_end = cur->frame->area.y + cur->frame->area.height;
+ his_offset = cur->frame->area.x;
+
+ if(his_offset - 1 < my_offset)
+ continue;
+
+ if(his_offset > dest)
+ continue;
+
+ if(his_edge_start >= my_edge_start &&
+ his_edge_start <= my_edge_end)
+ dest = his_offset;
+
+ if(my_edge_start >= his_edge_start &&
+ my_edge_start <= his_edge_end)
+ dest = his_offset;
+
+ }
+ break;
+ case OB_DIRECTION_NORTHEAST:
+ case OB_DIRECTION_SOUTHEAST:
+ case OB_DIRECTION_NORTHWEST:
+ case OB_DIRECTION_SOUTHWEST:
+ /* not implemented */
+ break;
+ default:
+ g_assert_not_reached();
+ }
+ return dest;
+}