Skip to content

Commit

Permalink
Add support for _NET_WM_FULLSCREEN_MONITORS
Browse files Browse the repository at this point in the history
This patch adds the _NET_WM_FULLSCREEN_MONITORS hint as defined in EWMH which is based on Xinerama.
It's allowing a window to be displayed fullscreen on multiple monitors.

Issue mentioned at #8
Specs at https://specifications.freedesktop.org/wm-spec/1.5/ar01s06.html#idm45763309187776
GTK test program used https://bugzilla.gnome.org/show_bug.cgi?id=414476
  • Loading branch information
dmaciejak authored and crmafra committed Feb 18, 2023
1 parent ac75047 commit 4d65834
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 1 deletion.
48 changes: 48 additions & 0 deletions src/actions.c
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,54 @@ void wUnmaximizeWindow(WWindow *wwin)
WMPostNotificationName(WMNChangedState, wwin, "maximize");
}

#ifdef USE_XINERAMA
void wFullscreenMonitorsWindow(WWindow *wwin, unsigned long top, unsigned long bottom,
unsigned long left, unsigned long right)
{
int i;
long monitor;
WMRect rect1, rect2;

if ((int)top < wwin->screen_ptr->xine_info.count &&
(int)bottom < wwin->screen_ptr->xine_info.count &&
(int)left < wwin->screen_ptr->xine_info.count &&
(int)right < wwin->screen_ptr->xine_info.count) {
wwin->flags.fullscreen_monitors[0] = top;
wwin->flags.fullscreen_monitors[1] = bottom;
wwin->flags.fullscreen_monitors[2] = left;
wwin->flags.fullscreen_monitors[3] = right;
} else {
wwin->flags.fullscreen_monitors[0] = -1;
return;
}

wwin->flags.fullscreen = True;
wWindowConfigureBorders(wwin);
ChangeStackingLevel(wwin->frame->core, WMFullscreenLevel);

wwin->bfs_geometry.x = wwin->frame_x;
wwin->bfs_geometry.y = wwin->frame_y;
wwin->bfs_geometry.width = wwin->frame->core->width;
wwin->bfs_geometry.height = wwin->frame->core->height;

i = 0;
monitor = wwin->flags.fullscreen_monitors[i];
rect1 = wwin->screen_ptr->xine_info.screens[monitor];

for (i = 1; i <= 3; i++) {
monitor = wwin->flags.fullscreen_monitors[i];
rect2 = wwin->screen_ptr->xine_info.screens[monitor];
wGetRectUnion(&rect1, &rect2, &rect1);
}
wWindowConfigure(wwin, rect1.pos.x, rect1.pos.y, rect1.size.width, rect1.size.height);

wwin->screen_ptr->bfs_focused_window = wwin->screen_ptr->focused_window;
wSetFocusTo(wwin->screen_ptr, wwin);

WMPostNotificationName(WMNChangedState, wwin, "fullscreen");
}
#endif

void wFullscreenWindow(WWindow *wwin)
{
int head;
Expand Down
3 changes: 3 additions & 0 deletions src/actions.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ void wArrangeIcons(WScreen *scr, Bool arrangeAll);

void wMakeWindowVisible(WWindow *wwin);

void wFullscreenMonitorsWindow(WWindow *wwin, unsigned long top, unsigned long bottom,
unsigned long left, unsigned long right);

void wFullscreenWindow(WWindow *wwin);
void wUnfullscreenWindow(WWindow *wwin);

Expand Down
3 changes: 3 additions & 0 deletions src/window.c
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,9 @@ WWindow *wManageWindow(WScreen *scr, Window window)
XChangeWindowAttributes(dpy, window, CWEventMask | CWDontPropagate | CWSaveUnder, &attribs);
XSetWindowBorderWidth(dpy, window, 0);

if (wwin->wm_class != NULL && strcmp(wwin->wm_class, "DockApp") != 0)
wwin->flags.fullscreen_monitors[0] = -1;

/* get hints from GNUstep app */
if (wwin->wm_class != NULL && strcmp(wwin->wm_class, "GNUstep") == 0)
wwin->flags.is_gnustep = 1;
Expand Down
1 change: 1 addition & 0 deletions src/window.h
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ typedef struct WWindow {
unsigned int maximized:7;
unsigned int old_maximized:7;
unsigned int fullscreen:1;
long fullscreen_monitors[4];
unsigned int omnipresent:1;
unsigned int semi_focused:1;
/* window type flags */
Expand Down
52 changes: 51 additions & 1 deletion src/wmspec.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ static Atom net_wm_visible_name; /* TODO (unnecessary?) */
static Atom net_wm_icon_name;
static Atom net_wm_visible_icon_name; /* TODO (unnecessary?) */
static Atom net_wm_desktop;
#ifdef USE_XINERAMA
static Atom net_wm_fullscreen_monitors;
#endif
static Atom net_wm_window_type;
static Atom net_wm_window_type_desktop;
static Atom net_wm_window_type_dock;
Expand Down Expand Up @@ -161,6 +164,9 @@ static atomitem_t atomNames[] = {
{"_NET_WM_ICON_NAME", &net_wm_icon_name},
{"_NET_WM_VISIBLE_ICON_NAME", &net_wm_visible_icon_name},
{"_NET_WM_DESKTOP", &net_wm_desktop},
#ifdef USE_XINERAMA
{"_NET_WM_FULLSCREEN_MONITORS", &net_wm_fullscreen_monitors},
#endif
{"_NET_WM_WINDOW_TYPE", &net_wm_window_type},
{"_NET_WM_WINDOW_TYPE_DESKTOP", &net_wm_window_type_desktop},
{"_NET_WM_WINDOW_TYPE_DOCK", &net_wm_window_type_dock},
Expand Down Expand Up @@ -287,6 +293,9 @@ static void setSupportedHints(WScreen *scr)
atom[i++] = net_wm_moveresize;
#endif
atom[i++] = net_wm_desktop;
#ifdef USE_XINERAMA
atom[i++] = net_wm_fullscreen_monitors;
#endif
atom[i++] = net_wm_window_type;
atom[i++] = net_wm_window_type_desktop;
atom[i++] = net_wm_window_type_dock;
Expand Down Expand Up @@ -1027,6 +1036,9 @@ static void updateStateHint(WWindow *wwin, Bool changedWorkspace, Bool del)
{ /* changeable */
if (del) {
XDeleteProperty(dpy, wwin->client_win, net_wm_state);
#ifdef USE_XINERAMA
XDeleteProperty(dpy, wwin->client_win, net_wm_fullscreen_monitors);
#endif
} else {
Atom state[15]; /* nr of defined state atoms */
int i = 0;
Expand Down Expand Up @@ -1067,6 +1079,20 @@ static void updateStateHint(WWindow *wwin, Bool changedWorkspace, Bool del)

XChangeProperty(dpy, wwin->client_win, net_wm_state, XA_ATOM, 32,
PropModeReplace, (unsigned char *)state, i);

#ifdef USE_XINERAMA
if (wwin->flags.fullscreen && (wwin->flags.fullscreen_monitors[0] != -1)) {
unsigned long data[4];

data[0] = wwin->flags.fullscreen_monitors[0];
data[1] = wwin->flags.fullscreen_monitors[1];
data[2] = wwin->flags.fullscreen_monitors[2];
data[3] = wwin->flags.fullscreen_monitors[3];

XChangeProperty(dpy, wwin->client_win, net_wm_fullscreen_monitors, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)data, 4);
}
#endif
}
}

Expand Down Expand Up @@ -1260,8 +1286,10 @@ static void doStateAtom(WWindow *wwin, Atom state, int set, Bool init)
} else {
if (set)
wFullscreenWindow(wwin);
else
else {
wUnfullscreenWindow(wwin);
wwin->flags.fullscreen_monitors[0] = -1;
}
}
} else if (state == net_wm_state_above) {
if (set == _NET_WM_STATE_TOGGLE)
Expand Down Expand Up @@ -1759,8 +1787,30 @@ Bool wNETWMProcessClientMessage(XClientMessageEvent *event)
wWindowChangeWorkspace(wwin, desktop);
}
return True;

#ifdef USE_XINERAMA
} else if (event->message_type == net_wm_fullscreen_monitors) {
unsigned long top, bottom, left, right, src_indication;

top = event->data.l[0];
bottom = event->data.l[1];
left = event->data.l[2];
right = event->data.l[3];
src_indication = event->data.l[4];

if (src_indication > 1)
wwarning("_NET_WM_FULLSCREEN_MONITORS source indication %ld not supported", src_indication);

wFullscreenMonitorsWindow(wwin, top, bottom, left, right);
return True;
}
#else
}
#endif

#ifdef DEBUG_WMSPEC
wmessage("processClientMessage unsupported type %s", XGetAtomName(dpy, event->message_type));
#endif
return False;
}

Expand Down
30 changes: 30 additions & 0 deletions src/xinerama.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,3 +410,33 @@ WMPoint wGetPointToCenterRectInHead(WScreen * scr, int head, int width, int heig

return p;
}

/* Find the bounding rect of the union of two rectangles */
void wGetRectUnion(const WMRect *rect1, const WMRect *rect2, WMRect *dest)
{
int dest_x, dest_y;
int dest_w, dest_h;

dest_x = rect1->pos.x;
dest_y = rect1->pos.y;
dest_w = rect1->size.width;
dest_h = rect1->size.height;

if (rect2->pos.x < dest_x) {
dest_w += dest_x - rect2->pos.x;
dest_x = rect2->pos.x;
}
if (rect2->pos.y < dest_y) {
dest_h += dest_y - rect2->pos.y;
dest_y = rect2->pos.y;
}
if (rect2->pos.x + rect2->size.width > dest_x + dest_w)
dest_w = rect2->pos.x + rect2->size.width - dest_x;
if (rect2->pos.y + rect2->size.height > dest_y + dest_h)
dest_h = rect2->pos.y + rect2->size.height - dest_y;

dest->pos.x = dest_x;
dest->pos.y = dest_y;
dest->size.width = dest_w;
dest->size.height = dest_h;
}
2 changes: 2 additions & 0 deletions src/xinerama.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ WArea wGetUsableAreaForHead(WScreen *scr, int head, WArea *totalAreaPtr, Bool no

WMPoint wGetPointToCenterRectInHead(WScreen *scr, int head, int width, int height);

void wGetRectUnion(const WMRect *rect1, const WMRect *rect2, WMRect *dest);

Bool wWindowTouchesHead(WWindow *wwin, int head);
Bool wAppIconTouchesHead(WAppIcon *aicon, int head);

Expand Down

0 comments on commit 4d65834

Please sign in to comment.