#include "frame.h"
#include "event.h"
#include "focus.h"
+#include "focus_cycle.h"
#include "popup.h"
-#include "render/render.h"
+#include "version.h"
+#include "obrender/render.h"
#include "gettext.h"
#include "obt/display.h"
+#include "obt/xqueue.h"
#include "obt/prop.h"
-#include "obt/mainloop.h"
#include <X11/Xlib.h>
#ifdef HAVE_UNISTD_H
static Size screen_physical_size;
static guint screen_old_desktop;
static gboolean screen_desktop_timeout = TRUE;
+static guint screen_desktop_timer = 0;
/*! An array of desktops, holding an array of areas per monitor */
static Rect *monitor_area = NULL;
/*! An array of desktops, holding an array of struts */
static GSList *struts_bottom = NULL;
static ObPagerPopup *desktop_popup;
+static guint desktop_popup_timer = 0;
+static gboolean desktop_popup_perm;
/*! The number of microseconds that you need to be on a desktop before it will
replace the remembered "last desktop" */
-#define REMEMBER_LAST_DESKTOP_TIME 750000
+#define REMEMBER_LAST_DESKTOP_TIME 750
static gboolean replace_wm(void)
{
current_wm_sn_owner = None;
}
- timestamp = event_get_server_time();
+ timestamp = event_time();
XSetSelectionOwner(obt_display, wm_sn_atom, screen_support_win,
timestamp);
/* Wait for old window manager to go away */
if (current_wm_sn_owner) {
- XEvent event;
gulong wait = 0;
const gulong timeout = G_USEC_PER_SEC * 15; /* wait for 15s max */
+ ObtXQueueWindowType wt;
+
+ wt.window = current_wm_sn_owner;
+ wt.type = DestroyNotify;
while (wait < timeout) {
- if (XCheckWindowEvent(obt_display, current_wm_sn_owner,
- StructureNotifyMask, &event) &&
- event.type == DestroyNotify)
+ /* Checks the local queue and incoming events for this event */
+ if (xqueue_exists_local(xqueue_match_window_type, &wt))
break;
g_usleep(G_USEC_PER_SEC / 10);
wait += G_USEC_PER_SEC / 10;
NET_SUPPORTING_WM_CHECK, WINDOW, screen_support_win);
/* set properties on the supporting window */
- OBT_PROP_SETS(screen_support_win, NET_WM_NAME, utf8, "Openbox");
+ OBT_PROP_SETS(screen_support_win, NET_WM_NAME, "Openbox");
OBT_PROP_SET32(screen_support_win, NET_SUPPORTING_WM_CHECK,
WINDOW, screen_support_win);
supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG);
supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL);
supported[i++] = OBT_PROP_ATOM(NET_WM_ALLOWED_ACTIONS);
+ supported[i++] = OBT_PROP_ATOM(NET_WM_WINDOW_OPACITY);
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
supported[i++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
supported[i++] = OBT_PROP_ATOM(OB_THEME);
supported[i++] = OBT_PROP_ATOM(OB_CONFIG_FILE);
supported[i++] = OBT_PROP_ATOM(OB_CONTROL);
- supported[i++] = OBT_PROP_ATOM(OB_ROLE);
- supported[i++] = OBT_PROP_ATOM(OB_NAME);
- supported[i++] = OBT_PROP_ATOM(OB_CLASS);
+ supported[i++] = OBT_PROP_ATOM(OB_VERSION);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_ROLE);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_TITLE);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_NAME);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_CLASS);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_NAME);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_GROUP_CLASS);
+ supported[i++] = OBT_PROP_ATOM(OB_APP_TYPE);
g_assert(i == num_support);
OBT_PROP_SETA32(obt_root(ob_screen),
NET_SUPPORTED, ATOM, supported, num_support);
g_free(supported);
+ OBT_PROP_SETS(RootWindow(obt_display, ob_screen), OB_VERSION,
+ OPENBOX_VERSION);
+
screen_tell_ksplash();
return TRUE;
gboolean namesexist = FALSE;
desktop_popup = pager_popup_new();
+ desktop_popup_perm = FALSE;
pager_popup_height(desktop_popup, POPUP_HEIGHT);
if (reconfig) {
screen_resize();
/* have names already been set for the desktops? */
- if (OBT_PROP_GETSS(obt_root(ob_screen), NET_DESKTOP_NAMES, utf8, &names)) {
+ if (OBT_PROP_GETSS_UTF8(obt_root(ob_screen), NET_DESKTOP_NAMES, &names)) {
g_strfreev(names);
namesexist = TRUE;
}
/* set the root window property */
OBT_PROP_SETSS(obt_root(ob_screen),
- NET_DESKTOP_NAMES, utf8, (const gchar**)names);
+ NET_DESKTOP_NAMES, (const gchar*const*)names);
g_strfreev(names);
}
void screen_resize(void)
{
- static gint oldw = 0, oldh = 0;
gint w, h;
GList *it;
gulong geometry[2];
w = WidthOfScreen(ScreenOfDisplay(obt_display, ob_screen));
h = HeightOfScreen(ScreenOfDisplay(obt_display, ob_screen));
- if (w == oldw && h == oldh) return;
-
- oldw = w; oldh = h;
-
/* Set the _NET_DESKTOP_GEOMETRY hint */
screen_physical_size.width = geometry[0] = w;
screen_physical_size.height = geometry[1] = h;
if (ob_state() != OB_STATE_RUNNING)
return;
- screen_update_areas();
+ /* this calls screen_update_areas(), which we need ! */
dock_configure();
- for (it = client_list; it; it = g_list_next(it))
+ for (it = client_list; it; it = g_list_next(it)) {
client_move_onscreen(it->data, FALSE);
+ client_reconfigure(it->data, FALSE);
+ }
}
void screen_set_num_desktops(guint num)
static gboolean last_desktop_func(gpointer data)
{
screen_desktop_timeout = TRUE;
- return FALSE;
+ screen_desktop_timer = 0;
+ return FALSE; /* don't repeat */
}
void screen_set_desktop(guint num, gboolean dofocus)
}
}
screen_desktop_timeout = FALSE;
- obt_main_loop_timeout_remove(ob_main_loop, last_desktop_func);
- obt_main_loop_timeout_add(ob_main_loop, REMEMBER_LAST_DESKTOP_TIME,
- last_desktop_func, NULL, NULL, NULL);
+ if (screen_desktop_timer) g_source_remove(screen_desktop_timer);
+ screen_desktop_timer = g_timeout_add(REMEMBER_LAST_DESKTOP_TIME,
+ last_desktop_func, NULL);
ob_debug("Moving to desktop %d", num+1);
if (ob_state() == OB_STATE_RUNNING)
- screen_show_desktop_popup(screen_desktop);
+ screen_show_desktop_popup(screen_desktop, FALSE);
/* ignore enter events caused by the move */
ignore_start = event_start_ignore_all_enters();
for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
if (WINDOW_IS_CLIENT(it->data)) {
ObClient *c = it->data;
- client_hide(c);
- if (c == focus_client) {
- /* c was focused and we didn't do fallback clearly so make sure
- openbox doesnt still consider the window focused.
- this happens when using NextWindow with allDesktops, since
- it doesnt want to move focus on desktop change, but the
- focus is not going to stay with the current window, which
- has now disappeared */
- focus_set_client(NULL);
+ if (client_hide(c)) {
+ if (c == focus_client) {
+ /* c was focused and we didn't do fallback clearly so make
+ sure openbox doesnt still consider the window focused.
+ this happens when using NextWindow with allDesktops,
+ since it doesnt want to move focus on desktop change,
+ but the focus is not going to stay with the current
+ window, which has now disappeared.
+ only do this if the client was actually hidden,
+ otherwise it can keep focus. */
+ focus_set_client(NULL);
+ }
}
}
}
+ focus_cycle_addremove(NULL, TRUE);
+
event_end_ignore_all_enters(ignore_start);
- if (event_curtime != CurrentTime)
- screen_desktop_user_time = event_curtime;
+ if (event_source_time() != CurrentTime)
+ screen_desktop_user_time = event_source_time();
}
void screen_add_desktop(gboolean current)
static gboolean hide_desktop_popup_func(gpointer data)
{
pager_popup_hide(desktop_popup);
+ desktop_popup_timer = 0;
return FALSE; /* don't repeat */
}
-void screen_show_desktop_popup(guint d)
+void screen_show_desktop_popup(guint d, gboolean perm)
{
- Rect *a;
+ const Rect *a;
/* 0 means don't show the popup */
if (!config_desktop_popup_time) return;
MAX(a->width/3, POPUP_WIDTH));
pager_popup_show(desktop_popup, screen_desktop_names[d], d);
- obt_main_loop_timeout_remove(ob_main_loop, hide_desktop_popup_func);
- obt_main_loop_timeout_add(ob_main_loop, config_desktop_popup_time * 1000,
- hide_desktop_popup_func, desktop_popup,
- g_direct_equal, NULL);
- g_free(a);
+ if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
+ desktop_popup_timer = 0;
+ if (!perm && !desktop_popup_perm)
+ /* only hide if its not already being show permanently */
+ desktop_popup_timer = g_timeout_add(config_desktop_popup_time,
+ hide_desktop_popup_func,
+ desktop_popup);
+ if (perm)
+ desktop_popup_perm = TRUE;
}
void screen_hide_desktop_popup(void)
{
- obt_main_loop_timeout_remove_data(ob_main_loop, hide_desktop_popup_func,
- desktop_popup, FALSE);
+ if (desktop_popup_timer) g_source_remove(desktop_popup_timer);
+ desktop_popup_timer = 0;
pager_popup_hide(desktop_popup);
+ desktop_popup_perm = FALSE;
}
guint screen_find_desktop(guint from, ObDirection dir,
screen_desktop_names = NULL;
if (OBT_PROP_GETSS(obt_root(ob_screen),
- NET_DESKTOP_NAMES, utf8, &screen_desktop_names))
+ NET_DESKTOP_NAMES, &screen_desktop_names))
for (i = 0; screen_desktop_names[i] && i < screen_num_desktops; ++i);
else
i = 0;
/* if we changed any names, then set the root property so we can
all agree on the names */
OBT_PROP_SETSS(obt_root(ob_screen), NET_DESKTOP_NAMES,
- utf8, (const gchar**)screen_desktop_names);
+ (const gchar*const*)screen_desktop_names);
}
/* resize the pager for these names */
} ObScreenStrut;
#define RESET_STRUT_LIST(sl) \
- (g_slist_free(sl), sl = NULL)
+ while (sl) { \
+ g_slice_free(ObScreenStrut, (sl)->data); \
+ sl = g_slist_delete_link(sl, sl); \
+ }
#define ADD_STRUT_TO_LIST(sl, d, s) \
{ \
- ObScreenStrut *ss = g_new(ObScreenStrut, 1); \
+ ObScreenStrut *ss = g_slice_new(ObScreenStrut); \
ss->desktop = d; \
ss->strut = s; \
sl = g_slist_prepend(sl, ss); \
static void get_xinerama_screens(Rect **xin_areas, guint *nxin)
{
guint i;
- gint n, l, r, t, b;
+ gint l, r, t, b;
#ifdef XINERAMA
+ gint n;
XineramaScreenInfo *info;
#endif
b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1);
}
RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1);
+
+ for (i = 0; i < *nxin; ++i)
+ ob_debug("Monitor %d @ %d,%d %dx%d\n", i,
+ (*xin_areas)[i].x, (*xin_areas)[i].y,
+ (*xin_areas)[i].width, (*xin_areas)[i].height);
+ ob_debug("Full desktop @ %d,%d %dx%d\n",
+ (*xin_areas)[i].x, (*xin_areas)[i].y,
+ (*xin_areas)[i].width, (*xin_areas)[i].height);
}
void screen_update_areas(void)
{
- guint j;
+ guint i;
gulong *dims;
- GList *it;
- GSList *sit;
+ GList *it, *onscreen;
+
+ /* collect the clients that are on screen */
+ onscreen = NULL;
+ for (it = client_list; it; it = g_list_next(it)) {
+ if (client_monitor(it->data) != screen_num_monitors)
+ onscreen = g_list_prepend(onscreen, it->data);
+ }
g_free(monitor_area);
get_xinerama_screens(&monitor_area, &screen_num_monitors);
config_margins.right_start = RECT_TOP(monitor_area[screen_num_monitors]);
config_margins.right_end = RECT_BOTTOM(monitor_area[screen_num_monitors]);
- dims = g_new(gulong, 4 * screen_num_desktops);
-
RESET_STRUT_LIST(struts_left);
RESET_STRUT_LIST(struts_top);
RESET_STRUT_LIST(struts_right);
VALIDATE_STRUTS(struts_bottom, bottom,
monitor_area[screen_num_monitors].height / 2);
- /* set up the work area to be full screen across all monitors */
- for (j = 0; j < screen_num_desktops; ++j) {
- dims[j*4 + 0] =
- monitor_area[screen_num_monitors].x;
- dims[j*4 + 1] =
- monitor_area[screen_num_monitors].y;
- dims[j*4 + 2] =
- monitor_area[screen_num_monitors].width;
- dims[j*4 + 3] =
- monitor_area[screen_num_monitors].height;
- }
-
- /* calculate the work area from the struts */
- for (j = 0; j < screen_num_desktops; ++j) {
- gint l = 0, r = 0, t = 0, b = 0;
-
- for (sit = struts_left; sit; sit = g_slist_next(sit)) {
- ObScreenStrut *s = sit->data;
- if (s->desktop == j || s->desktop == DESKTOP_ALL)
- l = MAX(l, s->strut->left);
- }
- for (sit = struts_top; sit; sit = g_slist_next(sit)) {
- ObScreenStrut *s = sit->data;
- if (s->desktop == j || s->desktop == DESKTOP_ALL)
- t = MAX(t, s->strut->top);
- }
- for (sit = struts_right; sit; sit = g_slist_next(sit)) {
- ObScreenStrut *s = sit->data;
- if (s->desktop == j || s->desktop == DESKTOP_ALL)
- r = MAX(r, s->strut->right);
- }
- for (sit = struts_bottom; sit; sit = g_slist_next(sit)) {
- ObScreenStrut *s = sit->data;
- if (s->desktop == j || s->desktop == DESKTOP_ALL)
- b = MAX(b, s->strut->bottom);
- }
-
- /* based on these margins, set the work area for the desktop */
- dims[j*4 + 0] += l;
- dims[j*4 + 1] += t;
- dims[j*4 + 2] -= l + r;
- dims[j*4 + 3] -= t + b;
+ dims = g_new(gulong, 4 * screen_num_desktops);
+ for (i = 0; i < screen_num_desktops; ++i) {
+ Rect *area = screen_area(i, SCREEN_AREA_ALL_MONITORS, NULL);
+ dims[i*4+0] = area->x;
+ dims[i*4+1] = area->y;
+ dims[i*4+2] = area->width;
+ dims[i*4+3] = area->height;
+ g_slice_free(Rect, area);
}
/* set the legacy workarea hint to the union of all the monitors */
dims, 4 * screen_num_desktops);
/* the area has changed, adjust all the windows if they need it */
- for (it = client_list; it; it = g_list_next(it))
+ for (it = onscreen; it; it = g_list_next(it))
client_reconfigure(it->data, FALSE);
g_free(dims);
}
}
- a = g_new(Rect, 1);
+ a = g_slice_new(Rect);
a->x = l;
a->y = t;
a->width = r - l + 1;
return a;
}
-guint screen_find_monitor(Rect *search)
+typedef struct {
+ Rect r;
+ gboolean subtract;
+} RectArithmetic;
+
+guint screen_find_monitor(const Rect *search)
{
guint i;
- guint most = screen_num_monitors;
- guint mostv = 0;
+ guint mostpx_index = screen_num_monitors;
+ guint mostpx = 0;
+ guint closest_distance_index = screen_num_monitors;
+ guint closest_distance = G_MAXUINT;
+ GSList *counted = NULL;
+
+ /* we want to count the number of pixels search has on each monitor, but not
+ double count. so if a pixel is counted on monitor A then we should not
+ count it again on monitor B. in the end we want to return the monitor
+ that had the most pixels counted under this scheme.
+
+ this assumes that monitors earlier in the list are more desirable to be
+ considered the search area's monitor. we try the configured primary
+ monitor first, so it gets the highest preference.
+
+ if we have counted an area A, then we want to subtract the intersection
+ of A with the area on future monitors.
+ but now consider if we count an area B that intersects A. we want to
+ subtract the area B from that counted on future monitors, but not
+ subtract the intersection of A and B twice! so we would add the
+ intersection of A and B back, to account for it being subtracted both
+ for A and B.
+
+ this is the idea behind the algorithm. we always subtract the full area
+ for monitor M intersected with the search area. we'll call that AREA.
+ but then we go through the list |counted| and for each rectangle in
+ the list that is being subtracted from future monitors, we insert a
+ request to add back the intersection of the subtracted rect with AREA.
+ vice versa for a rect in |counted| that is getting added back.
+ */
+
+ if (config_primary_monitor_index < screen_num_monitors) {
+ const Rect *monitor;
+ Rect on_current_monitor;
+ glong area;
+
+ monitor = screen_physical_area_monitor(config_primary_monitor_index);
+
+ if (RECT_INTERSECTS_RECT(*monitor, *search)) {
+ RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
+ area = RECT_AREA(on_current_monitor);
+
+ if (area > mostpx) {
+ mostpx = area;
+ mostpx_index = config_primary_monitor_index;
+ }
+
+ /* add the intersection rect on the current monitor to the
+ counted list. that's easy for the first one, we just mark it for
+ subtraction */
+ {
+ RectArithmetic *ra = g_slice_new(RectArithmetic);
+ ra->r = on_current_monitor;
+ ra->subtract = TRUE;
+ counted = g_slist_prepend(counted, ra);
+ }
+ }
+ }
for (i = 0; i < screen_num_monitors; ++i) {
- Rect *area = screen_physical_area_monitor(i);
- if (RECT_INTERSECTS_RECT(*area, *search)) {
- Rect r;
- guint v;
+ const Rect *monitor;
+ Rect on_current_monitor;
+ glong area;
+ GSList *it;
- RECT_SET_INTERSECTION(r, *area, *search);
- v = r.width * r.height;
+ monitor = screen_physical_area_monitor(i);
- if (v > mostv) {
- mostv = v;
- most = i;
+ if (!RECT_INTERSECTS_RECT(*monitor, *search)) {
+ /* If we don't intersect then find the distance between the search
+ rect and the monitor. We'll use the closest monitor from this
+ metric if none of the monitors intersect. */
+ guint distance = rect_manhatten_distance(*monitor, *search);
+
+ if (distance < closest_distance) {
+ closest_distance = distance;
+ closest_distance_index = i;
}
+ continue;
+ }
+
+ if (i == config_primary_monitor_index)
+ continue; /* already did this one */
+
+ RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search);
+ area = RECT_AREA(on_current_monitor);
+
+ /* remove pixels we already counted on any previous monitors. */
+ for (it = counted; it; it = g_slist_next(it)) {
+ RectArithmetic *ra = it->data;
+ Rect intersection;
+
+ RECT_SET_INTERSECTION(intersection, ra->r, *search);
+ if (ra->subtract) area -= RECT_AREA(intersection);
+ else area += RECT_AREA(intersection);
+ }
+
+ if (area > mostpx) {
+ mostpx = area;
+ mostpx_index = i;
}
- g_free(area);
+
+ /* add the intersection rect on the current monitor I to the counted
+ list.
+ but now we need to compensate for every rectangle R already in the
+ counted list, and add a new rect R' that is the intersection of
+ R and I, but with the reverse subtraction/addition operation.
+ */
+ for (it = counted; it; it = g_slist_next(it)) {
+ RectArithmetic *saved = it->data;
+
+ if (!RECT_INTERSECTS_RECT(saved->r, on_current_monitor))
+ continue;
+ /* we are going to subtract our rect from future monitors, but
+ part of it may already be being subtracted/added, so compensate
+ to not double add/subtract. */
+ RectArithmetic *reverse = g_slice_new(RectArithmetic);
+ RECT_SET_INTERSECTION(reverse->r, saved->r, on_current_monitor);
+ reverse->subtract = !saved->subtract;
+ /* prepend so we can continue thru the list uninterupted */
+ counted = g_slist_prepend(counted, reverse);
+ }
+ {
+ RectArithmetic *ra = g_slice_new(RectArithmetic);
+ ra->r = on_current_monitor;
+ ra->subtract = TRUE;
+ counted = g_slist_prepend(counted, ra);
+ }
+ }
+
+ while (counted) {
+ g_slice_free(RectArithmetic, counted->data);
+ counted = g_slist_delete_link(counted, counted);
}
- return most;
+
+ if (mostpx_index < screen_num_monitors)
+ return mostpx_index;
+
+ g_assert(closest_distance_index < screen_num_monitors);
+ return closest_distance_index;
}
-Rect* screen_physical_area_all_monitors(void)
+const Rect* screen_physical_area_all_monitors(void)
{
return screen_physical_area_monitor(screen_num_monitors);
}
-Rect* screen_physical_area_monitor(guint head)
+const Rect* screen_physical_area_monitor(guint head)
{
- Rect *a;
g_assert(head <= screen_num_monitors);
- a = g_new(Rect, 1);
- *a = monitor_area[head];
- return a;
+ return &monitor_area[head];
}
gboolean screen_physical_area_monitor_contains(guint head, Rect *search)
return screen_monitor_pointer();
}
-Rect* screen_physical_area_active(void)
+const Rect* screen_physical_area_active(void)
{
return screen_physical_area_monitor(screen_monitor_active());
}
return screen_monitor_pointer();
}
-Rect *screen_physical_area_primary(gboolean fixed)
+const Rect* screen_physical_area_primary(gboolean fixed)
{
return screen_physical_area_monitor(screen_monitor_primary(fixed));
}
}
return ret;
}
+
+gboolean screen_compare_desktops(guint a, guint b)
+{
+ if (a == DESKTOP_ALL)
+ a = screen_desktop;
+ if (b == DESKTOP_ALL)
+ b = screen_desktop;
+ return a == b;
+}