static void screen_tell_ksplash(void);
static void screen_fallback_focus(void);
-guint screen_num_desktops;
-guint screen_num_monitors;
-guint screen_desktop;
-guint screen_last_desktop;
-gboolean screen_showing_desktop;
-ObDesktopLayout screen_desktop_layout;
-gchar **screen_desktop_names;
-Window screen_support_win;
-Time screen_desktop_user_time = CurrentTime;
+guint screen_num_desktops;
+guint screen_num_monitors;
+guint screen_desktop;
+guint screen_last_desktop;
+ObScreenShowDestopMode screen_show_desktop_mode;
+ObDesktopLayout screen_desktop_layout;
+gchar **screen_desktop_names;
+Window screen_support_win;
+Time screen_desktop_user_time = CurrentTime;
static Size screen_physical_size;
static guint screen_old_desktop;
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_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);
screen_last_desktop = screen_desktop;
/* don't start in showing-desktop mode */
- screen_showing_desktop = FALSE;
+ screen_show_desktop_mode = SCREEN_SHOW_DESKTOP_NO;
OBT_PROP_SET32(obt_root(ob_screen),
- NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop);
+ NET_SHOWING_DESKTOP, CARDINAL, screen_showing_desktop());
if (session_desktop_layout_present &&
screen_validate_layout(&session_desktop_layout))
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)
screen_num_desktops);
}
-void screen_show_desktop(gboolean show, ObClient *show_only)
+void screen_show_desktop(ObScreenShowDestopMode show_mode, ObClient *show_only)
{
GList *it;
- if (show == screen_showing_desktop) return; /* no change */
+ ObScreenShowDestopMode before_mode = screen_show_desktop_mode;
+
+ gboolean showing_before = screen_showing_desktop();
+ screen_show_desktop_mode = show_mode;
+ gboolean showing_after = screen_showing_desktop();
- screen_showing_desktop = show;
+ if (showing_before == showing_after) {
+ /* No change. */
+ screen_show_desktop_mode = before_mode;
+ return;
+ }
- if (show) {
+ if (screen_show_desktop_mode == SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE &&
+ show_only != NULL)
+ {
+ /* If we're showing the desktop until the show-mode is toggled, we
+ don't allow breaking out of showing-desktop mode unless we're
+ showing all the windows again. */
+ screen_show_desktop_mode = before_mode;
+ return;
+ }
+
+ if (showing_after) {
/* hide windows bottom to top */
for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) {
if (WINDOW_IS_CLIENT(it->data)) {
}
}
- if (show) {
+ if (showing_after) {
/* focus the desktop */
for (it = focus_order; it; it = g_list_next(it)) {
ObClient *c = it->data;
}
}
- show = !!show; /* make it boolean */
- OBT_PROP_SET32(obt_root(ob_screen), NET_SHOWING_DESKTOP, CARDINAL, show);
+ OBT_PROP_SET32(obt_root(ob_screen),
+ NET_SHOWING_DESKTOP,
+ CARDINAL,
+ !!showing_after);
+}
+
+gboolean screen_showing_desktop()
+{
+ switch (screen_show_desktop_mode) {
+ case SCREEN_SHOW_DESKTOP_NO:
+ return FALSE;
+ case SCREEN_SHOW_DESKTOP_UNTIL_WINDOW:
+ case SCREEN_SHOW_DESKTOP_UNTIL_TOGGLE:
+ return TRUE;
+ }
+ g_assert_not_reached();
+ return FALSE;
}
void screen_install_colormap(ObClient *client, gboolean install)
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)
dims, 4 * screen_num_desktops);
/* the area has changed, adjust all the windows if they need it */
- for (it = onscreen; it; it = g_list_next(it)) {
- client_move_onscreen(it->data, FALSE);
+ for (it = onscreen; it; it = g_list_next(it))
client_reconfigure(it->data, FALSE);
- }
g_free(dims);
}
return a;
}
+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;
+ glong 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) {
- const 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;
+
+ monitor = screen_physical_area_monitor(i);
- RECT_SET_INTERSECTION(r, *area, *search);
- v = r.width * r.height;
+ 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 (v > mostv) {
- mostv = v;
- most = i;
+ 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;
+ }
+
+ /* 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);
}
}
- return most;
+
+ while (counted) {
+ g_slice_free(RectArithmetic, counted->data);
+ counted = g_slist_delete_link(counted, counted);
+ }
+
+ if (mostpx_index < screen_num_monitors)
+ return mostpx_index;
+
+ g_assert(closest_distance_index < screen_num_monitors);
+ return closest_distance_index;
}
const Rect* screen_physical_area_all_monitors(void)
}
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;
+}
+
+void screen_apply_gravity_point(gint *x, gint *y, gint width, gint height,
+ const GravityPoint *position, const Rect *area)
+{
+ if (position->x.center)
+ *x = area->width / 2 - width / 2;
+ else {
+ *x = position->x.pos;
+ if (position->x.denom)
+ *x = (*x * area->width) / position->x.denom;
+ if (position->x.opposite)
+ *x = area->width - width - *x;
+ }
+
+ if (position->y.center)
+ *y = area->height / 2 - height / 2;
+ else {
+ *y = position->y.pos;
+ if (position->y.denom)
+ *y = (*y * area->height) / position->y.denom;
+ if (position->y.opposite)
+ *y = area->height - height - *y;
+ }
+
+ *x += area->x;
+ *y += area->y;
+}