/* * Copyright (C) 2003 Sun Microsystems, Inc. * Copyright (C) 2003,2004 Rob Adams * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301, USA. * Authors: * Mark McLoughlin */ #include #include "panel-struts.h" #include "panel-multiscreen.h" #include "panel-xutils.h" typedef struct { PanelToplevel *toplevel; GdkScreen *screen; int monitor; PanelOrientation orientation; GdkRectangle geometry; int strut_size; int strut_start; int strut_end; GdkRectangle allocated_geometry; int allocated_strut_size; int allocated_strut_start; int allocated_strut_end; } PanelStrut; static GSList *panel_struts_list = NULL; static inline PanelStrut * panel_struts_find_strut (PanelToplevel *toplevel) { GSList *l; for (l = panel_struts_list; l; l = l->next) { PanelStrut *strut = l->data; if (strut->toplevel == toplevel) break; } return l ? l->data : NULL; } static void panel_struts_get_monitor_geometry (GdkScreen *screen, int monitor, int *x, int *y, int *width, int *height) { *x = panel_multiscreen_x (screen, monitor); *y = panel_multiscreen_y (screen, monitor); *width = panel_multiscreen_width (screen, monitor); *height = panel_multiscreen_height (screen, monitor); } static PanelStrut * panel_struts_intersect (GSList *struts, GdkRectangle *geometry, int skip) { GSList *l; int i; i = 0; for (l = struts; l; l = l->next) { PanelStrut *strut = l->data; int x1, y1, x2, y2; x1 = MAX (strut->allocated_geometry.x, geometry->x); y1 = MAX (strut->allocated_geometry.y, geometry->y); x2 = MIN (strut->allocated_geometry.x + strut->allocated_geometry.width, geometry->x + geometry->width); y2 = MIN (strut->allocated_geometry.y + strut->allocated_geometry.height, geometry->y + geometry->height); if (x2 - x1 > 0 && y2 - y1 > 0 && ++i > skip) break; } return l ? l->data : NULL; } static int panel_struts_allocation_overlapped (PanelStrut *strut, PanelStrut *overlap, GdkRectangle *geometry, gboolean *moved_down, int skip) { int overlap_x1, overlap_y1, overlap_x2, overlap_y2; overlap_x1 = overlap->allocated_geometry.x; overlap_y1 = overlap->allocated_geometry.y; overlap_x2 = overlap->allocated_geometry.x + overlap->allocated_geometry.width; overlap_y2 = overlap->allocated_geometry.y + overlap->allocated_geometry.height; if (strut->orientation == overlap->orientation) { int old_x, old_y; old_x = geometry->x; old_y = geometry->y; switch (strut->orientation) { case PANEL_ORIENTATION_TOP: geometry->y = overlap_y2; strut->allocated_strut_size += geometry->y - old_y; break; case PANEL_ORIENTATION_BOTTOM: geometry->y = overlap_y1 - geometry->height; strut->allocated_strut_size += old_y - geometry->y; break; case PANEL_ORIENTATION_LEFT: geometry->x = overlap_x2; strut->allocated_strut_size += geometry->x - old_x; break; case PANEL_ORIENTATION_RIGHT: geometry->x = overlap_x1 - geometry->width; strut->allocated_strut_size += old_x - geometry->x; break; default: g_assert_not_reached (); break; } } else { if (strut->orientation & PANEL_HORIZONTAL_MASK || overlap->orientation & PANEL_VERTICAL_MASK) return ++skip; switch (overlap->orientation) { case PANEL_ORIENTATION_TOP: geometry->y = overlap_y2; *moved_down = TRUE; break; case PANEL_ORIENTATION_BOTTOM: if (!*moved_down) geometry->y = overlap_y1 - geometry->height; else if (overlap_y1 > geometry->y) geometry->height = overlap_y1 - geometry->y; else return ++skip; break; default: g_assert_not_reached (); break; } strut->allocated_strut_start = geometry->y; strut->allocated_strut_end = geometry->y + geometry->height - 1; } return skip; } static gboolean panel_struts_allocate_struts (PanelToplevel *toplevel, GdkScreen *screen, int monitor) { GSList *allocated = NULL; GSList *l; gboolean toplevel_changed = FALSE; for (l = panel_struts_list; l; l = l->next) { PanelStrut *strut = l->data; PanelStrut *overlap; GdkRectangle geometry; int monitor_x, monitor_y; int monitor_width, monitor_height; gboolean moved_down; int skip; if (strut->screen != screen || strut->monitor != monitor) continue; panel_struts_get_monitor_geometry (strut->screen, strut->monitor, &monitor_x, &monitor_y, &monitor_width, &monitor_height); strut->allocated_strut_size = strut->strut_size; strut->allocated_strut_start = strut->strut_start; strut->allocated_strut_end = strut->strut_end; geometry = strut->geometry; moved_down = FALSE; skip = 0; while ((overlap = panel_struts_intersect (allocated, &geometry, skip))) skip = panel_struts_allocation_overlapped ( strut, overlap, &geometry, &moved_down, skip); if (strut->orientation & PANEL_VERTICAL_MASK) { if (geometry.y < monitor_y) { geometry.height = geometry.y + geometry.height - monitor_y; geometry.y = monitor_y; } if (geometry.y + geometry.height > monitor_y + monitor_height) geometry.height = monitor_y + monitor_height - geometry.y; } if (strut->allocated_geometry.x != geometry.x || strut->allocated_geometry.y != geometry.y || strut->allocated_geometry.width != geometry.width || strut->allocated_geometry.height != geometry.height) { strut->allocated_geometry = geometry; if (strut->toplevel == toplevel) toplevel_changed = TRUE; else gtk_widget_queue_resize (GTK_WIDGET (strut->toplevel)); } allocated = g_slist_append (allocated, strut); } g_slist_free (allocated); return toplevel_changed; } void panel_struts_set_window_hint (PanelToplevel *toplevel) { GtkWidget *widget; PanelStrut *strut; int strut_size; int monitor_x, monitor_y, monitor_width, monitor_height; int screen_width, screen_height; int leftmost, rightmost, topmost, bottommost; widget = GTK_WIDGET (toplevel); if (!gtk_widget_get_realized (widget)) return; if (!(strut = panel_struts_find_strut (toplevel))) { panel_struts_unset_window_hint (toplevel); return; } strut_size = strut->allocated_strut_size; screen_width = gdk_screen_get_width (strut->screen); screen_height = gdk_screen_get_height (strut->screen); panel_struts_get_monitor_geometry (strut->screen, strut->monitor, &monitor_x, &monitor_y, &monitor_width, &monitor_height); panel_multiscreen_is_at_visible_extreme (strut->screen, strut->monitor, &leftmost, &rightmost, &topmost, &bottommost); switch (strut->orientation) { case PANEL_ORIENTATION_TOP: if (monitor_y > 0) strut_size += monitor_y; if (!topmost) strut_size = 0; break; case PANEL_ORIENTATION_BOTTOM: if (monitor_y + monitor_height < screen_height) strut_size += screen_height - (monitor_y + monitor_height); if (!bottommost) strut_size = 0; break; case PANEL_ORIENTATION_LEFT: if (leftmost && monitor_x > 0) strut_size += monitor_x; if (!leftmost) strut_size = 0; break; case PANEL_ORIENTATION_RIGHT: if (monitor_x + monitor_width < screen_width) strut_size += screen_width - (monitor_x + monitor_width); if (!rightmost) strut_size = 0; break; default: g_assert_not_reached (); break; } panel_xutils_set_strut (gtk_widget_get_window (widget), strut->orientation, strut_size, strut->allocated_strut_start, strut->allocated_strut_end); } void panel_struts_unset_window_hint (PanelToplevel *toplevel) { if (!gtk_widget_get_realized (GTK_WIDGET (toplevel))) return; panel_xutils_set_strut (gtk_widget_get_window (GTK_WIDGET (toplevel)), 0, 0, 0, 0); } static inline int orientation_to_order (PanelOrientation orientation) { switch (orientation) { case PANEL_ORIENTATION_TOP: return 1; case PANEL_ORIENTATION_BOTTOM: return 2; case PANEL_ORIENTATION_LEFT: return 3; case PANEL_ORIENTATION_RIGHT: return 4; default: g_assert_not_reached (); return -1; } } static inline int get_toplevel_depth (PanelToplevel *toplevel) { int depth = 0; while ((toplevel = panel_toplevel_get_attach_toplevel (toplevel))) depth++; return depth; } /* Sort in order of * 1) screen * 2) monitor * 3) depth (for drawers) * 4) top, bottom, left, right * 5) strut_start ascending * 6) strut_end descending */ static int panel_struts_compare (const PanelStrut *s1, const PanelStrut *s2) { int s1_depth; int s2_depth; if (s1->screen != s2->screen) return gdk_screen_get_number (s1->screen) - gdk_screen_get_number (s2->screen); if (s1->monitor != s2->monitor) return s1->monitor - s2->monitor; s1_depth = get_toplevel_depth (s1->toplevel); s2_depth = get_toplevel_depth (s2->toplevel); if (s1_depth != s2_depth) return s2_depth - s1_depth; if (s1->orientation != s2->orientation) return orientation_to_order (s1->orientation) - orientation_to_order (s2->orientation); if (s1->strut_start != s2->strut_start) return s1->strut_start - s2->strut_start; if (s1->strut_end != s2->strut_end) return s2->strut_end - s1->strut_end; return 0; } gboolean panel_struts_register_strut (PanelToplevel *toplevel, GdkScreen *screen, int monitor, PanelOrientation orientation, int strut_size, int strut_start, int strut_end) { PanelStrut *strut; gboolean new_strut = FALSE; int monitor_x, monitor_y, monitor_width, monitor_height; if (!(strut = panel_struts_find_strut (toplevel))) { strut = g_new0 (PanelStrut, 1); new_strut = TRUE; } else if (strut->toplevel == toplevel && strut->orientation == orientation && strut->screen == screen && strut->monitor == monitor && strut->strut_size == strut_size && strut->strut_start == strut_start && strut->strut_end == strut_end) return FALSE; strut->toplevel = toplevel; strut->orientation = orientation; strut->screen = screen; strut->monitor = monitor; strut->strut_size = strut_size; strut->strut_start = strut_start; strut->strut_end = strut_end; panel_struts_get_monitor_geometry (screen, monitor, &monitor_x, &monitor_y, &monitor_width, &monitor_height); switch (strut->orientation) { case PANEL_ORIENTATION_TOP: strut->geometry.x = strut->strut_start; strut->geometry.y = monitor_y; strut->geometry.width = strut->strut_end - strut->strut_start + 1; strut->geometry.height = strut->strut_size; break; case PANEL_ORIENTATION_BOTTOM: strut->geometry.x = strut->strut_start; strut->geometry.y = monitor_y + monitor_height - strut->strut_size; strut->geometry.width = strut->strut_end - strut->strut_start + 1; strut->geometry.height = strut->strut_size; break; case PANEL_ORIENTATION_LEFT: strut->geometry.x = monitor_x; strut->geometry.y = strut->strut_start; strut->geometry.width = strut->strut_size; strut->geometry.height = strut->strut_end - strut->strut_start + 1; break; case PANEL_ORIENTATION_RIGHT: strut->geometry.x = monitor_x + monitor_width - strut->strut_size; strut->geometry.y = strut->strut_start; strut->geometry.width = strut->strut_size; strut->geometry.height = strut->strut_end - strut->strut_start + 1; break; } if (new_strut) panel_struts_list = g_slist_append (panel_struts_list, strut); panel_struts_list = g_slist_sort (panel_struts_list, (GCompareFunc) panel_struts_compare); return panel_struts_allocate_struts (toplevel, screen, monitor); } void panel_struts_unregister_strut (PanelToplevel *toplevel) { PanelStrut *strut; GdkScreen *screen; int monitor; if (!(strut = panel_struts_find_strut (toplevel))) return; screen = strut->screen; monitor = strut->monitor; panel_struts_list = g_slist_remove (panel_struts_list, strut); g_free (strut); panel_struts_allocate_struts (toplevel, screen, monitor); } gboolean panel_struts_update_toplevel_geometry (PanelToplevel *toplevel, int *x, int *y, int *width, int *height) { PanelStrut *strut; g_return_val_if_fail (x != NULL, FALSE); g_return_val_if_fail (y != NULL, FALSE); if (!(strut = panel_struts_find_strut (toplevel))) return FALSE; *x += strut->allocated_geometry.x - strut->geometry.x; *y += strut->allocated_geometry.y - strut->geometry.y; if (width != NULL && *width != -1) *width += strut->allocated_geometry.width - strut->geometry.width; if (height != NULL && *height != -1) *height += strut->allocated_geometry.height - strut->geometry.height; return TRUE; }