mirror of
https://github.com/gryf/wmaker.git
synced 2026-05-02 06:10:52 +02:00
Compare commits
5 Commits
329f82f6e7
...
1f03c13f4d
| Author | SHA1 | Date | |
|---|---|---|---|
| 1f03c13f4d | |||
| 8e84264036 | |||
| 4b4abf4c50 | |||
| a631e3060e | |||
| 6c5c3e6181 |
@@ -132,6 +132,11 @@ static struct expert_option {
|
|||||||
|
|
||||||
{ N_("Allow windows to take focus using mouse wheel."),
|
{ N_("Allow windows to take focus using mouse wheel."),
|
||||||
/* default: */ False, OPTION_WMAKER, "MouseWheelFocus"},
|
/* default: */ False, OPTION_WMAKER, "MouseWheelFocus"},
|
||||||
|
|
||||||
|
#ifdef USE_RANDR
|
||||||
|
{ N_("Automatically (de)activate monitors on hotplug events."),
|
||||||
|
/* default: */ False, OPTION_WMAKER, "HotplugMonitor"},
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -657,11 +657,11 @@ dnl RandR support
|
|||||||
dnl =============
|
dnl =============
|
||||||
m4_divert_push([INIT_PREPARE])dnl
|
m4_divert_push([INIT_PREPARE])dnl
|
||||||
AC_ARG_ENABLE([randr],
|
AC_ARG_ENABLE([randr],
|
||||||
[AS_HELP_STRING([--enable-randr], [enable RandR extension support (NOT recommended, buggy)])],
|
[AS_HELP_STRING([--enable-randr], [enable RandR extension support for multi-monitor live reconfiguration])],
|
||||||
[AS_CASE(["$enableval"],
|
[AS_CASE(["$enableval"],
|
||||||
[yes|no], [],
|
[yes|no], [],
|
||||||
[AC_MSG_ERROR([bad value $enableval for --enable-randr]) ]) ],
|
[AC_MSG_ERROR([bad value $enableval for --enable-randr]) ]) ],
|
||||||
[enable_randr=no])
|
[enable_randr=auto])
|
||||||
m4_divert_pop([INIT_PREPARE])dnl
|
m4_divert_pop([INIT_PREPARE])dnl
|
||||||
WM_XEXT_CHECK_XRANDR
|
WM_XEXT_CHECK_XRANDR
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ POTFILES = \
|
|||||||
$(top_srcdir)/src/framewin.c \
|
$(top_srcdir)/src/framewin.c \
|
||||||
$(top_srcdir)/src/geomview.c \
|
$(top_srcdir)/src/geomview.c \
|
||||||
$(top_srcdir)/src/icon.c \
|
$(top_srcdir)/src/icon.c \
|
||||||
|
$(top_srcdir)/src/keytree.c \
|
||||||
$(top_srcdir)/src/main.c \
|
$(top_srcdir)/src/main.c \
|
||||||
$(top_srcdir)/src/menu.c \
|
$(top_srcdir)/src/menu.c \
|
||||||
$(top_srcdir)/src/misc.c \
|
$(top_srcdir)/src/misc.c \
|
||||||
@@ -40,6 +41,7 @@ POTFILES = \
|
|||||||
$(top_srcdir)/src/pixmap.c \
|
$(top_srcdir)/src/pixmap.c \
|
||||||
$(top_srcdir)/src/placement.c \
|
$(top_srcdir)/src/placement.c \
|
||||||
$(top_srcdir)/src/properties.c \
|
$(top_srcdir)/src/properties.c \
|
||||||
|
$(top_srcdir)/src/randr.c \
|
||||||
$(top_srcdir)/src/resources.c \
|
$(top_srcdir)/src/resources.c \
|
||||||
$(top_srcdir)/src/rootmenu.c \
|
$(top_srcdir)/src/rootmenu.c \
|
||||||
$(top_srcdir)/src/screen.c \
|
$(top_srcdir)/src/screen.c \
|
||||||
|
|||||||
+3
-2
@@ -61,6 +61,8 @@ wmaker_SOURCES = \
|
|||||||
placement.h \
|
placement.h \
|
||||||
properties.c \
|
properties.c \
|
||||||
properties.h \
|
properties.h \
|
||||||
|
randr.c \
|
||||||
|
randr.h \
|
||||||
resources.c \
|
resources.c \
|
||||||
resources.h \
|
resources.h \
|
||||||
rootmenu.c \
|
rootmenu.c \
|
||||||
@@ -195,6 +197,5 @@ defaults-callbacks-dynamic:
|
|||||||
--source "$(srcdir)/defaults.c" --structure "optionList" \
|
--source "$(srcdir)/defaults.c" --structure "optionList" \
|
||||||
--field-value-ptr 4 --field-callback 5 \
|
--field-value-ptr 4 --field-callback 5 \
|
||||||
--struct-def "wPreferences=$(srcdir)/WindowMaker.h" \
|
--struct-def "wPreferences=$(srcdir)/WindowMaker.h" \
|
||||||
--struct-def "legacy_minipreview_config=$(srcdir)/defaults.c" \
|
--callback "getBool=char, getEnum=char, getInt=int, getString=char*" \
|
||||||
--callback "getBool=char, getEnum=char, getInt=int" \
|
|
||||||
--callback "getPathList=char*, getCoord=WCoord"
|
--callback "getPathList=char*, getCoord=WCoord"
|
||||||
|
|||||||
@@ -410,6 +410,9 @@ extern struct WPreferences {
|
|||||||
|
|
||||||
char dont_blink; /* do not blink icon selection */
|
char dont_blink; /* do not blink icon selection */
|
||||||
char keep_dock_on_primary_head; /* keep dock on primary head */
|
char keep_dock_on_primary_head; /* keep dock on primary head */
|
||||||
|
#ifdef USE_RANDR
|
||||||
|
char hotplug_monitor; /* auto-(de)activate monitors */
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Appearance options */
|
/* Appearance options */
|
||||||
char new_style; /* Use newstyle buttons */
|
char new_style; /* Use newstyle buttons */
|
||||||
|
|||||||
+7
-1
@@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1997-2003 Alfredo K. Kojima
|
* Copyright (c) 1997-2003 Alfredo K. Kojima
|
||||||
* Copyright (c) 1998-2003 Dan Pascu
|
* Copyright (c) 1998-2003 Dan Pascu
|
||||||
* Copyright (c) 2014-2023 Window Maker Team
|
* Copyright (c) 2014-2026 Window Maker Team
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -1608,6 +1608,9 @@ void wDeiconifyWindow(WWindow *wwin)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Relocate to an active head if the stored position is in dead space */
|
||||||
|
wWindowSnapToHead(wwin);
|
||||||
|
|
||||||
/* if the window is in another workspace, do it silently */
|
/* if the window is in another workspace, do it silently */
|
||||||
if (!netwm_hidden) {
|
if (!netwm_hidden) {
|
||||||
#ifdef USE_ANIMATIONS
|
#ifdef USE_ANIMATIONS
|
||||||
@@ -1876,6 +1879,9 @@ static void unhideWindow(WIcon *icon, int icon_x, int icon_y, WWindow *wwin, int
|
|||||||
|
|
||||||
wwin->flags.hidden = 0;
|
wwin->flags.hidden = 0;
|
||||||
|
|
||||||
|
/* Relocate to an active head if the stored position is in dead space */
|
||||||
|
wWindowSnapToHead(wwin);
|
||||||
|
|
||||||
#ifdef USE_ANIMATIONS
|
#ifdef USE_ANIMATIONS
|
||||||
if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations && animate) {
|
if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations && animate) {
|
||||||
animateResize(wwin->screen_ptr, icon_x, icon_y,
|
animateResize(wwin->screen_ptr, icon_x, icon_y,
|
||||||
|
|||||||
+4
-1
@@ -5,7 +5,6 @@
|
|||||||
* Copyright (c) 1997-2003 Alfredo K. Kojima
|
* Copyright (c) 1997-2003 Alfredo K. Kojima
|
||||||
* Copyright (c) 1998-2003 Dan Pascu
|
* Copyright (c) 1998-2003 Dan Pascu
|
||||||
* Copyright (c) 2014-2026 Window Maker Team
|
* Copyright (c) 2014-2026 Window Maker Team
|
||||||
|
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -527,6 +526,10 @@ WDefaultEntry optionList[] = {
|
|||||||
{"KeepDockOnPrimaryHead", "NO", NULL,
|
{"KeepDockOnPrimaryHead", "NO", NULL,
|
||||||
&wPreferences.keep_dock_on_primary_head, getBool, updateDock,
|
&wPreferences.keep_dock_on_primary_head, getBool, updateDock,
|
||||||
NULL, NULL},
|
NULL, NULL},
|
||||||
|
#ifdef USE_RANDR
|
||||||
|
{"HotplugMonitor", "NO", NULL,
|
||||||
|
&wPreferences.hotplug_monitor, getBool, NULL, NULL, NULL},
|
||||||
|
#endif
|
||||||
{"HotCorners", "NO", NULL,
|
{"HotCorners", "NO", NULL,
|
||||||
&wPreferences.hot_corners, getBool, NULL, NULL, NULL},
|
&wPreferences.hot_corners, getBool, NULL, NULL, NULL},
|
||||||
{"HotCornerDelay", "250", (void *)&wPreferences.hot_corner_delay,
|
{"HotCornerDelay", "250", (void *)&wPreferences.hot_corner_delay,
|
||||||
|
|||||||
+37
@@ -3054,6 +3054,43 @@ void wDockSwap(WDock *dock)
|
|||||||
wScreenUpdateUsableArea(scr);
|
wScreenUpdateUsableArea(scr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Snap a clip back onto a visible head after a RandR reconfiguration,
|
||||||
|
* preserving its relative position within that head */
|
||||||
|
void wClipSnapToHead(WDock *clip)
|
||||||
|
{
|
||||||
|
WScreen *scr = clip->screen_ptr;
|
||||||
|
WMRect rect, head;
|
||||||
|
float rel_x, rel_y;
|
||||||
|
int x = clip->x_pos;
|
||||||
|
int y = clip->y_pos;
|
||||||
|
|
||||||
|
/* Already fully on a visible head, nothing to do */
|
||||||
|
if (!wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE))
|
||||||
|
return;
|
||||||
|
|
||||||
|
rect.pos.x = clip->x_pos;
|
||||||
|
rect.pos.y = clip->y_pos;
|
||||||
|
rect.size.width = ICON_SIZE;
|
||||||
|
rect.size.height = ICON_SIZE;
|
||||||
|
|
||||||
|
/* Find the nearest remaining head to the clip's old position */
|
||||||
|
head = wGetRectForHead(scr, wGetHeadForRect(scr, rect));
|
||||||
|
|
||||||
|
/* Compute fractional position within that head and clamp to [0..1] */
|
||||||
|
rel_x = (float)(clip->x_pos - head.pos.x) / (float)head.size.width;
|
||||||
|
rel_y = (float)(clip->y_pos - head.pos.y) / (float)head.size.height;
|
||||||
|
|
||||||
|
if (rel_x < 0.0f) rel_x = 0.0f;
|
||||||
|
else if (rel_x > 1.0f) rel_x = 1.0f;
|
||||||
|
if (rel_y < 0.0f) rel_y = 0.0f;
|
||||||
|
else if (rel_y > 1.0f) rel_y = 1.0f;
|
||||||
|
|
||||||
|
x = head.pos.x + (int)(rel_x * (head.size.width - ICON_SIZE));
|
||||||
|
y = head.pos.y + (int)(rel_y * (head.size.height - ICON_SIZE));
|
||||||
|
|
||||||
|
moveDock(clip, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
static pid_t execCommand(WAppIcon *btn, const char *command, WSavedState *state)
|
static pid_t execCommand(WAppIcon *btn, const char *command, WSavedState *state)
|
||||||
{
|
{
|
||||||
WScreen *scr = btn->icon->core->screen_ptr;
|
WScreen *scr = btn->icon->core->screen_ptr;
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ WAppIcon *wDockFindIconForWindow(WDock *dock, Window window);
|
|||||||
void wDockDoAutoLaunch(WDock *dock, int workspace);
|
void wDockDoAutoLaunch(WDock *dock, int workspace);
|
||||||
void wDockLaunchWithState(WAppIcon *btn, WSavedState *state);
|
void wDockLaunchWithState(WAppIcon *btn, WSavedState *state);
|
||||||
void wDockSwap(WDock *dock);
|
void wDockSwap(WDock *dock);
|
||||||
|
void wClipSnapToHead(WDock *clip);
|
||||||
|
|
||||||
#ifdef USE_DOCK_XDND
|
#ifdef USE_DOCK_XDND
|
||||||
int wDockReceiveDNDDrop(WScreen *scr, XEvent *event);
|
int wDockReceiveDNDDrop(WScreen *scr, XEvent *event);
|
||||||
|
|||||||
+10
-14
@@ -3,7 +3,7 @@
|
|||||||
* Window Maker window manager
|
* Window Maker window manager
|
||||||
*
|
*
|
||||||
* Copyright (c) 1997-2003 Alfredo K. Kojima
|
* Copyright (c) 1997-2003 Alfredo K. Kojima
|
||||||
* Copyright (c) 2014-2023 Window Maker Team
|
* Copyright (c) 2014-2026 Window Maker Team
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_RANDR
|
#ifdef USE_RANDR
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include "randr.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <X11/XKBlib.h>
|
#include <X11/XKBlib.h>
|
||||||
@@ -271,10 +271,6 @@ void DispatchEvent(XEvent * event)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case ConfigureNotify:
|
case ConfigureNotify:
|
||||||
#ifdef USE_RANDR
|
|
||||||
if (event->xconfigure.window == DefaultRootWindow(dpy))
|
|
||||||
XRRUpdateConfiguration(event);
|
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SelectionRequest:
|
case SelectionRequest:
|
||||||
@@ -618,14 +614,14 @@ static void handleExtensions(XEvent * event)
|
|||||||
#endif /*KEEP_XKB_LOCK_STATUS */
|
#endif /*KEEP_XKB_LOCK_STATUS */
|
||||||
}
|
}
|
||||||
#ifdef USE_RANDR
|
#ifdef USE_RANDR
|
||||||
if (w_global.xext.randr.supported && event->type == (w_global.xext.randr.event_base + RRScreenChangeNotify)) {
|
if (w_global.xext.randr.supported &&
|
||||||
/* From xrandr man page: "Clients must call back into Xlib using
|
(event->type == (w_global.xext.randr.event_base + RRScreenChangeNotify) ||
|
||||||
* XRRUpdateConfiguration when screen configuration change notify
|
event->type == (w_global.xext.randr.event_base + RRNotify))) {
|
||||||
* events are generated */
|
WScreen *randr_scr;
|
||||||
XRRUpdateConfiguration(event);
|
|
||||||
WCHANGE_STATE(WSTATE_RESTARTING);
|
randr_scr = wScreenForRootWindow(event->xany.window);
|
||||||
Shutdown(WSRestartPreparationMode);
|
if (randr_scr)
|
||||||
Restart(NULL,True);
|
wRandRHandleNotify(randr_scr, event);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-9
@@ -457,11 +457,10 @@ static void drawTransparentFrame(WWindow * wwin, int x, int y, int width, int he
|
|||||||
GC gc = wwin->screen_ptr->frame_gc;
|
GC gc = wwin->screen_ptr->frame_gc;
|
||||||
int h = 0;
|
int h = 0;
|
||||||
int bottom = 0;
|
int bottom = 0;
|
||||||
|
int fb = 0;
|
||||||
|
|
||||||
if (HAS_BORDER_WITH_SELECT(wwin)) {
|
if (HAS_BORDER_WITH_SELECT(wwin))
|
||||||
x += wwin->screen_ptr->frame_border_width;
|
fb = wwin->screen_ptr->frame_border_width;
|
||||||
y += wwin->screen_ptr->frame_border_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (HAS_TITLEBAR(wwin) && !wwin->flags.shaded) {
|
if (HAS_TITLEBAR(wwin) && !wwin->flags.shaded) {
|
||||||
h = WMFontHeight(wwin->screen_ptr->title_font) + (wPreferences.window_title_clearance +
|
h = WMFontHeight(wwin->screen_ptr->title_font) + (wPreferences.window_title_clearance +
|
||||||
@@ -478,13 +477,13 @@ static void drawTransparentFrame(WWindow * wwin, int x, int y, int width, int he
|
|||||||
(e.g. interactive placement), frame does not point to anything. */
|
(e.g. interactive placement), frame does not point to anything. */
|
||||||
bottom = RESIZEBAR_HEIGHT;
|
bottom = RESIZEBAR_HEIGHT;
|
||||||
}
|
}
|
||||||
XDrawRectangle(dpy, root, gc, x - 1, y - 1, width + 1, height + 1);
|
XDrawRectangle(dpy, root, gc, x, y, width - 1 + 2 * fb, height - 1 + 2 * fb);
|
||||||
|
|
||||||
if (h > 0) {
|
if (h > 0) {
|
||||||
XDrawLine(dpy, root, gc, x, y + h - 1, x + width, y + h - 1);
|
XDrawLine(dpy, root, gc, x, y + fb + h - 1, x + 2 * fb + width, y + fb + h - 1);
|
||||||
}
|
}
|
||||||
if (bottom > 0) {
|
if (bottom > 0) {
|
||||||
XDrawLine(dpy, root, gc, x, y + height - bottom, x + width, y + height - bottom);
|
XDrawLine(dpy, root, gc, x, y + fb + height - bottom, x + 2 * fb + width, y + fb + height - bottom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1200,7 +1199,7 @@ updateWindowPosition(WWindow * wwin, MoveData * data, Bool doResistance,
|
|||||||
static void draw_snap_frame(WWindow *wwin, int direction)
|
static void draw_snap_frame(WWindow *wwin, int direction)
|
||||||
{
|
{
|
||||||
WScreen *scr;
|
WScreen *scr;
|
||||||
int head, x, y;
|
int head, x, y, fb;
|
||||||
unsigned int width, height;
|
unsigned int width, height;
|
||||||
WMRect rect;
|
WMRect rect;
|
||||||
|
|
||||||
@@ -1212,6 +1211,7 @@ static void draw_snap_frame(WWindow *wwin, int direction)
|
|||||||
y = rect.pos.y;
|
y = rect.pos.y;
|
||||||
width = rect.size.width;
|
width = rect.size.width;
|
||||||
height = rect.size.height;
|
height = rect.size.height;
|
||||||
|
fb = HAS_BORDER_WITH_SELECT(wwin) ? 2 * wwin->screen_ptr->frame_border_width : 0;
|
||||||
|
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case SNAP_LEFT:
|
case SNAP_LEFT:
|
||||||
@@ -1258,7 +1258,7 @@ static void draw_snap_frame(WWindow *wwin, int direction)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
drawTransparentFrame(wwin, x, y, width, height);
|
drawTransparentFrame(wwin, x, y, width - fb, height - fb);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int get_snap_direction(WScreen *scr, int x, int y)
|
static int get_snap_direction(WScreen *scr, int x, int y)
|
||||||
|
|||||||
+629
@@ -0,0 +1,629 @@
|
|||||||
|
/* randr.c - RandR multi-monitor support
|
||||||
|
*
|
||||||
|
* Window Maker window manager
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 Window Maker Team
|
||||||
|
*
|
||||||
|
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "wconfig.h"
|
||||||
|
|
||||||
|
#ifdef USE_RANDR
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
|
||||||
|
#include "randr.h"
|
||||||
|
#include "window.h"
|
||||||
|
#include "framewin.h"
|
||||||
|
#include "xinerama.h"
|
||||||
|
#include "wmspec.h"
|
||||||
|
#include "dock.h"
|
||||||
|
#include "workspace.h"
|
||||||
|
#include "actions.h"
|
||||||
|
|
||||||
|
#define RANDR_MAX_OUTPUTS 32
|
||||||
|
#define RANDR_DEBOUNCE_DELAY 100 /* milliseconds */
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
RROutput xid;
|
||||||
|
RRCrtc crtc;
|
||||||
|
int x, y, w, h;
|
||||||
|
Rotation rotation; /* current CRTC rotation */
|
||||||
|
Bool connected; /* RR_Connected */
|
||||||
|
Bool stale; /* mark-scan scratch flag */
|
||||||
|
char name[64];
|
||||||
|
} WRandROutput;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
WRandROutput outputs[RANDR_MAX_OUTPUTS];
|
||||||
|
int n_outputs;
|
||||||
|
} WRandRState;
|
||||||
|
|
||||||
|
/* Initial output scan to populate state from scratch */
|
||||||
|
static void wRandR_Scan(WScreen *scr, WRandRState *state)
|
||||||
|
{
|
||||||
|
XRRScreenResources *sr;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
state->n_outputs = 0;
|
||||||
|
|
||||||
|
sr = XRRGetScreenResourcesCurrent(dpy, scr->root_win);
|
||||||
|
if (!sr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (i = 0; i < sr->noutput && state->n_outputs < RANDR_MAX_OUTPUTS; i++) {
|
||||||
|
XRROutputInfo *oi;
|
||||||
|
WRandROutput *o;
|
||||||
|
|
||||||
|
oi = XRRGetOutputInfo(dpy, sr, sr->outputs[i]);
|
||||||
|
if (!oi)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
o = &state->outputs[state->n_outputs++];
|
||||||
|
memset(o, 0, sizeof(*o));
|
||||||
|
o->xid = sr->outputs[i];
|
||||||
|
o->connected = (oi->connection == RR_Connected);
|
||||||
|
o->crtc = oi->crtc;
|
||||||
|
strncpy(o->name, oi->name, sizeof(o->name) - 1);
|
||||||
|
|
||||||
|
if (oi->crtc != None) {
|
||||||
|
XRRCrtcInfo *ci = XRRGetCrtcInfo(dpy, sr, oi->crtc);
|
||||||
|
if (ci) {
|
||||||
|
o->x = ci->x;
|
||||||
|
o->y = ci->y;
|
||||||
|
o->w = ci->width;
|
||||||
|
o->h = ci->height;
|
||||||
|
o->rotation = ci->rotation;
|
||||||
|
XRRFreeCrtcInfo(ci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XRRFreeOutputInfo(oi);
|
||||||
|
}
|
||||||
|
|
||||||
|
XRRFreeScreenResources(sr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Primary head detection */
|
||||||
|
static int wRandR_PrimaryHeadIndex(Window root, const RROutput *xids, int count)
|
||||||
|
{
|
||||||
|
RROutput primary_xid = XRRGetOutputPrimary(dpy, root);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (primary_xid != None) {
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (xids[i] == primary_xid)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Push RandR geometry into the Xinerama head layer */
|
||||||
|
static void wRandR_ApplyToXinerama(WScreen *scr, WRandRState *state)
|
||||||
|
{
|
||||||
|
WMRect new_screens[RANDR_MAX_OUTPUTS];
|
||||||
|
RROutput new_xids[RANDR_MAX_OUTPUTS]; /* parallel: XID of each head */
|
||||||
|
int count = 0;
|
||||||
|
int old_count;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < state->n_outputs && count < RANDR_MAX_OUTPUTS; i++) {
|
||||||
|
WRandROutput *o = &state->outputs[i];
|
||||||
|
int j, dup = 0;
|
||||||
|
|
||||||
|
/* Collect outputs that are truly active */
|
||||||
|
if (o->crtc == None || o->w == 0 || o->h == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Deduplicate mirrored outputs that share the same CRTC origin */
|
||||||
|
for (j = 0; j < count; j++) {
|
||||||
|
if (new_screens[j].pos.x == o->x && new_screens[j].pos.y == o->y) {
|
||||||
|
dup = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (dup)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
new_screens[count].pos.x = o->x;
|
||||||
|
new_screens[count].pos.y = o->y;
|
||||||
|
new_screens[count].size.width = o->w;
|
||||||
|
new_screens[count].size.height = o->h;
|
||||||
|
new_xids[count] = o->xid;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fallback: if every output disappeared, use the full virtual screen so
|
||||||
|
* we never end up with zero heads */
|
||||||
|
if (count == 0) {
|
||||||
|
new_screens[0].pos.x = 0;
|
||||||
|
new_screens[0].pos.y = 0;
|
||||||
|
new_screens[0].size.width = scr->scr_width;
|
||||||
|
new_screens[0].size.height = scr->scr_height;
|
||||||
|
new_xids[0] = None;
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
old_count = wXineramaHeads(scr);
|
||||||
|
|
||||||
|
/* Replace the screens array */
|
||||||
|
if (scr->xine_info.screens)
|
||||||
|
wfree(scr->xine_info.screens);
|
||||||
|
|
||||||
|
scr->xine_info.screens = wmalloc(sizeof(WMRect) * (count + 1));
|
||||||
|
memcpy(scr->xine_info.screens, new_screens, sizeof(WMRect) * count);
|
||||||
|
scr->xine_info.count = count;
|
||||||
|
|
||||||
|
scr->xine_info.primary_head = wRandR_PrimaryHeadIndex(scr->root_win, new_xids, count);
|
||||||
|
|
||||||
|
/* Refresh cached screen dimensions (updated by XRRUpdateConfiguration) */
|
||||||
|
scr->scr_width = WidthOfScreen(ScreenOfDisplay(dpy, scr->screen));
|
||||||
|
scr->scr_height = HeightOfScreen(ScreenOfDisplay(dpy, scr->screen));
|
||||||
|
|
||||||
|
/* Reallocate per-head usable area arrays if the head count changed */
|
||||||
|
if (old_count != count) {
|
||||||
|
wfree(scr->usableArea);
|
||||||
|
wfree(scr->totalUsableArea);
|
||||||
|
scr->usableArea = wmalloc(sizeof(WArea) * count);
|
||||||
|
scr->totalUsableArea = wmalloc(sizeof(WArea) * count);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Seed usable areas from new geometry */
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
WMRect *r = &scr->xine_info.screens[i];
|
||||||
|
|
||||||
|
scr->usableArea[i].x1 = scr->totalUsableArea[i].x1 = r->pos.x;
|
||||||
|
scr->usableArea[i].y1 = scr->totalUsableArea[i].y1 = r->pos.y;
|
||||||
|
scr->usableArea[i].x2 = scr->totalUsableArea[i].x2 = r->pos.x + r->size.width;
|
||||||
|
scr->usableArea[i].y2 = scr->totalUsableArea[i].y2 = r->pos.y + r->size.height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Bring any off-screen windows in */
|
||||||
|
static void wRandR_BringAllWindowsInside(WScreen *scr)
|
||||||
|
{
|
||||||
|
WWindow *wwin;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (wwin = scr->focused_window; wwin != NULL; wwin = wwin->prev) {
|
||||||
|
int bw, wx, wy, ww, wh;
|
||||||
|
int fully_inside = 0;
|
||||||
|
|
||||||
|
if (!wwin->flags.mapped || wwin->flags.hidden)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bw = HAS_BORDER(wwin) ? scr->frame_border_width : 0;
|
||||||
|
wx = wwin->frame_x - bw;
|
||||||
|
wy = wwin->frame_y - bw;
|
||||||
|
ww = (int)wwin->frame->core->width + 2 * bw;
|
||||||
|
wh = (int)wwin->frame->core->height + 2 * bw;
|
||||||
|
|
||||||
|
/* Skip windows already fully contained within a surviving head */
|
||||||
|
for (i = 0; i < wXineramaHeads(scr); i++) {
|
||||||
|
WMRect r = wGetRectForHead(scr, i);
|
||||||
|
if (wx >= r.pos.x && wy >= r.pos.y &&
|
||||||
|
wx + ww <= r.pos.x + (int)r.size.width &&
|
||||||
|
wy + wh <= r.pos.y + (int)r.size.height) {
|
||||||
|
fully_inside = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fully_inside)
|
||||||
|
wWindowSnapToHead(wwin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auto-deactivate a physically disconnected output that still holds a CRTC
|
||||||
|
*
|
||||||
|
* When a cable is pulled or monitor turned off, the X server sets
|
||||||
|
* connection=RR_Disconnected but does NOT free the CRTC automatically.
|
||||||
|
* We must call XRRSetCrtcConfig with mode=None to release it,
|
||||||
|
* which causes the server to fire a fresh batch of RandR events.
|
||||||
|
*
|
||||||
|
* Returns True if at least one CRTC was freed */
|
||||||
|
static Bool wRandR_AutoDeactivate(WScreen *scr, WRandRState *state, XRRScreenResources *sr)
|
||||||
|
{
|
||||||
|
Bool deactivated = False;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < state->n_outputs; i++) {
|
||||||
|
WRandROutput *o = &state->outputs[i];
|
||||||
|
Status ret;
|
||||||
|
|
||||||
|
if (o->connected || o->crtc == None || o->stale)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Release the CRTC: no mode, no outputs */
|
||||||
|
ret = XRRSetCrtcConfig(dpy, sr, o->crtc, sr->timestamp,
|
||||||
|
0, 0, None, RR_Rotate_0, NULL, 0);
|
||||||
|
if (ret == RRSetConfigSuccess) {
|
||||||
|
wwarning("RandR: released CRTC for disconnected output %s", o->name);
|
||||||
|
o->crtc = None;
|
||||||
|
o->w = o->h = 0;
|
||||||
|
deactivated = True;
|
||||||
|
} else {
|
||||||
|
wwarning("RandR: failed to release CRTC for output %s", o->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compact and shrink the virtual framebuffer to the bounding box of remaining active outputs */
|
||||||
|
if (deactivated) {
|
||||||
|
int min_x = scr->scr_width;
|
||||||
|
int max_x = 0, max_y = 0;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (i = 0; i < state->n_outputs; i++) {
|
||||||
|
WRandROutput *a = &state->outputs[i];
|
||||||
|
|
||||||
|
if (a->crtc == None || a->w == 0 || a->h == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (a->x < min_x) min_x = a->x;
|
||||||
|
if (a->x + a->w > max_x) max_x = a->x + a->w;
|
||||||
|
if (a->y + a->h > max_y) max_y = a->y + a->h;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Slide surviving outputs left to eliminate dead space at the origin */
|
||||||
|
if (min_x > 0 && max_x > 0) {
|
||||||
|
for (i = 0; i < state->n_outputs; i++) {
|
||||||
|
WRandROutput *a = &state->outputs[i];
|
||||||
|
XRRCrtcInfo *ci;
|
||||||
|
|
||||||
|
if (a->crtc == None || a->w == 0 || a->h == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ci = XRRGetCrtcInfo(dpy, sr, a->crtc);
|
||||||
|
if (!ci)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
XRRSetCrtcConfig(dpy, sr, a->crtc, sr->timestamp,
|
||||||
|
a->x - min_x, a->y,
|
||||||
|
ci->mode, ci->rotation,
|
||||||
|
ci->outputs, ci->noutput);
|
||||||
|
|
||||||
|
for (j = 0; j < state->n_outputs; j++) {
|
||||||
|
if (state->outputs[j].crtc == a->crtc)
|
||||||
|
state->outputs[j].x -= min_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
XRRFreeCrtcInfo(ci);
|
||||||
|
}
|
||||||
|
max_x -= min_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Shrink the virtual framebuffer down to the compacted bounding box */
|
||||||
|
if (max_x > 0 && max_y > 0 &&
|
||||||
|
(max_x < scr->scr_width || max_y < scr->scr_height)) {
|
||||||
|
int mm_w = (int)((long)WidthMMOfScreen(ScreenOfDisplay(dpy, scr->screen))
|
||||||
|
* max_x / scr->scr_width);
|
||||||
|
int mm_h = (int)((long)HeightMMOfScreen(ScreenOfDisplay(dpy, scr->screen))
|
||||||
|
* max_y / scr->scr_height);
|
||||||
|
XRRSetScreenSize(dpy, scr->root_win, max_x, max_y, mm_w, mm_h);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deactivated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Auto-activate a newly connected output that has no CRTC assigned yet
|
||||||
|
*
|
||||||
|
* Mirrors "xrandr --auto": find a free CRTC, pick the preferred mode,
|
||||||
|
* place the new head to the right of all active outputs,
|
||||||
|
* expand the virtual framebuffer if needed, then call XRRSetCrtcConfig.
|
||||||
|
*
|
||||||
|
* Returns True if at least one output was activated */
|
||||||
|
static Bool wRandR_AutoActivate(WScreen *scr, WRandRState *state, XRRScreenResources *sr)
|
||||||
|
{
|
||||||
|
Bool activated = False;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
for (i = 0; i < state->n_outputs; i++) {
|
||||||
|
WRandROutput *o = &state->outputs[i];
|
||||||
|
XRROutputInfo *oi;
|
||||||
|
RRCrtc free_crtc = None;
|
||||||
|
Rotation rotation = RR_Rotate_0;
|
||||||
|
RRMode best_mode = None;
|
||||||
|
int mode_w = 0, mode_h = 0;
|
||||||
|
int new_x = 0;
|
||||||
|
int new_vw, new_vh;
|
||||||
|
Status ret;
|
||||||
|
|
||||||
|
/* Only process newly connected outputs that have no CRTC yet */
|
||||||
|
if (!o->connected || o->crtc != None)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
oi = XRRGetOutputInfo(dpy, sr, o->xid);
|
||||||
|
if (!oi)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (oi->ncrtc == 0 || oi->nmode == 0) {
|
||||||
|
XRRFreeOutputInfo(oi);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Find a free CRTC that is idle and capable of driving this output */
|
||||||
|
for (j = 0; j < oi->ncrtc; j++) {
|
||||||
|
XRRCrtcInfo *ci = XRRGetCrtcInfo(dpy, sr, oi->crtcs[j]);
|
||||||
|
if (ci) {
|
||||||
|
if (ci->noutput == 0) {
|
||||||
|
int k;
|
||||||
|
|
||||||
|
for (k = 0; k < (int)ci->npossible; k++) {
|
||||||
|
if (ci->possible[k] == o->xid) {
|
||||||
|
free_crtc = oi->crtcs[j];
|
||||||
|
if (ci->rotations & RR_Rotate_0)
|
||||||
|
rotation = RR_Rotate_0;
|
||||||
|
else if (ci->rotations & RR_Rotate_90)
|
||||||
|
rotation = RR_Rotate_90;
|
||||||
|
else if (ci->rotations & RR_Rotate_180)
|
||||||
|
rotation = RR_Rotate_180;
|
||||||
|
else
|
||||||
|
rotation = RR_Rotate_270;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
XRRFreeCrtcInfo(ci);
|
||||||
|
}
|
||||||
|
if (free_crtc != None)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (free_crtc == None) {
|
||||||
|
wwarning("RandR: no free CRTC for output %s", o->name);
|
||||||
|
XRRFreeOutputInfo(oi);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Preferred mode is first in the list (per RandR spec) */
|
||||||
|
best_mode = oi->modes[0];
|
||||||
|
XRRFreeOutputInfo(oi);
|
||||||
|
|
||||||
|
/* Look up its pixel dimensions in sr->modes[] */
|
||||||
|
for (j = 0; j < sr->nmode; j++) {
|
||||||
|
if (sr->modes[j].id == best_mode) {
|
||||||
|
mode_w = (int)sr->modes[j].width;
|
||||||
|
mode_h = (int)sr->modes[j].height;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode_w == 0 || mode_h == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Position it to the right of all currently active outputs, same as GNOME */
|
||||||
|
for (j = 0; j < state->n_outputs; j++) {
|
||||||
|
WRandROutput *a = &state->outputs[j];
|
||||||
|
|
||||||
|
if (a->crtc != None && a->w > 0) {
|
||||||
|
int right = a->x + a->w;
|
||||||
|
|
||||||
|
if (right > new_x)
|
||||||
|
new_x = right;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Expand virtual framebuffer if the new head exceeds current size */
|
||||||
|
new_vw = new_x + mode_w;
|
||||||
|
new_vh = mode_h > scr->scr_height ? mode_h : scr->scr_height;
|
||||||
|
|
||||||
|
if (new_vw > scr->scr_width || new_vh > scr->scr_height) {
|
||||||
|
int mm_w = (int)((long)WidthMMOfScreen(ScreenOfDisplay(dpy, scr->screen))
|
||||||
|
* new_vw / scr->scr_width);
|
||||||
|
int mm_h = (int)((long)HeightMMOfScreen(ScreenOfDisplay(dpy, scr->screen))
|
||||||
|
* new_vh / scr->scr_height);
|
||||||
|
XRRSetScreenSize(dpy, scr->root_win, new_vw, new_vh, mm_w, mm_h);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = XRRSetCrtcConfig(dpy, sr, free_crtc, sr->timestamp,
|
||||||
|
new_x, 0, best_mode, rotation,
|
||||||
|
&o->xid, 1);
|
||||||
|
if (ret == RRSetConfigSuccess) {
|
||||||
|
wwarning("RandR: auto-activated output %s at +%d+0", o->name, new_x);
|
||||||
|
activated = True;
|
||||||
|
} else {
|
||||||
|
wwarning("RandR: failed to auto-activate output %s", o->name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Synchronize RandR state with the X server */
|
||||||
|
static void wRandR_Update(WScreen *scr)
|
||||||
|
{
|
||||||
|
WRandRState *state = (WRandRState *)scr->randr_state;
|
||||||
|
XRRScreenResources *sr;
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
/* Assume all outputs removed until server confirms */
|
||||||
|
for (i = 0; i < state->n_outputs; i++)
|
||||||
|
state->outputs[i].stale = True;
|
||||||
|
|
||||||
|
/* Re-query server, update or add outputs */
|
||||||
|
sr = XRRGetScreenResourcesCurrent(dpy, scr->root_win);
|
||||||
|
if (!sr) {
|
||||||
|
wwarning("wRandR_Update: XRRGetScreenResourcesCurrent failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < sr->noutput; i++) {
|
||||||
|
XRROutputInfo *oi;
|
||||||
|
WRandROutput *found = NULL;
|
||||||
|
|
||||||
|
oi = XRRGetOutputInfo(dpy, sr, sr->outputs[i]);
|
||||||
|
if (!oi)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Match by XID (stable across reconfigures) */
|
||||||
|
for (j = 0; j < state->n_outputs; j++) {
|
||||||
|
if (state->outputs[j].xid == sr->outputs[i]) {
|
||||||
|
found = &state->outputs[j];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Append new output not seen before */
|
||||||
|
if (!found && state->n_outputs < RANDR_MAX_OUTPUTS) {
|
||||||
|
found = &state->outputs[state->n_outputs++];
|
||||||
|
memset(found, 0, sizeof(*found));
|
||||||
|
found->xid = sr->outputs[i];
|
||||||
|
strncpy(found->name, oi->name, sizeof(found->name) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
found->stale = False;
|
||||||
|
found->connected = (oi->connection == RR_Connected);
|
||||||
|
found->crtc = oi->crtc;
|
||||||
|
found->w = 0;
|
||||||
|
found->h = 0;
|
||||||
|
found->rotation = RR_Rotate_0;
|
||||||
|
|
||||||
|
if (oi->crtc != None) {
|
||||||
|
XRRCrtcInfo *ci = XRRGetCrtcInfo(dpy, sr, oi->crtc);
|
||||||
|
if (ci) {
|
||||||
|
found->x = ci->x;
|
||||||
|
found->y = ci->y;
|
||||||
|
found->w = ci->width;
|
||||||
|
found->h = ci->height;
|
||||||
|
found->rotation = ci->rotation;
|
||||||
|
XRRFreeCrtcInfo(ci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XRRFreeOutputInfo(oi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* When HotplugMonitor is enabled, actively manage CRTC lifecycle */
|
||||||
|
if (wPreferences.hotplug_monitor) {
|
||||||
|
Bool changed;
|
||||||
|
|
||||||
|
changed = wRandR_AutoDeactivate(scr, state, sr);
|
||||||
|
changed |= wRandR_AutoActivate(scr, state, sr);
|
||||||
|
if (changed) {
|
||||||
|
XRRFreeScreenResources(sr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XRRFreeScreenResources(sr);
|
||||||
|
|
||||||
|
/* Remove outputs not reported by server at all */
|
||||||
|
for (i = 0, j = 0; i < state->n_outputs; i++) {
|
||||||
|
if (!state->outputs[i].stale) {
|
||||||
|
if (j != i)
|
||||||
|
state->outputs[j] = state->outputs[i];
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state->n_outputs = j;
|
||||||
|
|
||||||
|
/* Apply new geometry to the Xinerama head layer */
|
||||||
|
i = scr->xine_info.primary_head;
|
||||||
|
wRandR_ApplyToXinerama(scr, state);
|
||||||
|
|
||||||
|
/* Move the dock if needed */
|
||||||
|
if (scr->dock &&
|
||||||
|
((scr->xine_info.primary_head != i && wPreferences.keep_dock_on_primary_head) ||
|
||||||
|
scr->dock->x_pos < 0 ||
|
||||||
|
scr->dock->x_pos >= scr->scr_width))
|
||||||
|
wDockSwap(scr->dock);
|
||||||
|
|
||||||
|
/* Snap each workspace clip back onto a visible head if it ended up
|
||||||
|
* outside the (now smaller or rearranged) virtual framebuffer */
|
||||||
|
if (!wPreferences.flags.noclip) {
|
||||||
|
int k;
|
||||||
|
|
||||||
|
for (k = 0; k < scr->workspace_count; k++) {
|
||||||
|
WDock *clip = scr->workspaces[k]->clip;
|
||||||
|
|
||||||
|
if (clip)
|
||||||
|
wClipSnapToHead(clip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Refresh usable areas and EWMH hints */
|
||||||
|
wScreenUpdateUsableArea(scr);
|
||||||
|
wNETWMUpdateWorkarea(scr);
|
||||||
|
|
||||||
|
/* Bring any windows that ended up in dead space to an active head */
|
||||||
|
wRandR_BringAllWindowsInside(scr);
|
||||||
|
|
||||||
|
/* Rearrange miniaturized and appicons into the icon yard */
|
||||||
|
wArrangeIcons(scr, True);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wRandRDebounceTimerFired(void *data)
|
||||||
|
{
|
||||||
|
WScreen *scr = (WScreen *)data;
|
||||||
|
|
||||||
|
scr->randr_debounce_timer = NULL;
|
||||||
|
wRandR_Update(scr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end of local stuff */
|
||||||
|
|
||||||
|
void wRandRInit(WScreen *scr)
|
||||||
|
{
|
||||||
|
WRandRState *state;
|
||||||
|
|
||||||
|
if (!w_global.xext.randr.supported)
|
||||||
|
return;
|
||||||
|
|
||||||
|
state = wmalloc(sizeof(WRandRState));
|
||||||
|
memset(state, 0, sizeof(*state));
|
||||||
|
scr->randr_state = state;
|
||||||
|
|
||||||
|
wRandR_Scan(scr, state);
|
||||||
|
wRandR_ApplyToXinerama(scr, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wRandRTeardown(WScreen *scr)
|
||||||
|
{
|
||||||
|
if (!scr->randr_state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (scr->randr_debounce_timer) {
|
||||||
|
WMDeleteTimerHandler(scr->randr_debounce_timer);
|
||||||
|
scr->randr_debounce_timer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wfree(scr->randr_state);
|
||||||
|
scr->randr_state = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wRandRHandleNotify(WScreen *scr, XEvent *event)
|
||||||
|
{
|
||||||
|
if (!scr || !scr->randr_state)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (event->type == (w_global.xext.randr.event_base + RRScreenChangeNotify))
|
||||||
|
XRRUpdateConfiguration(event);
|
||||||
|
|
||||||
|
/* Debounce: cancel any pending timer, then restart it */
|
||||||
|
if (scr->randr_debounce_timer) {
|
||||||
|
WMDeleteTimerHandler(scr->randr_debounce_timer);
|
||||||
|
scr->randr_debounce_timer = NULL;
|
||||||
|
}
|
||||||
|
scr->randr_debounce_timer =
|
||||||
|
WMAddTimerHandler(RANDR_DEBOUNCE_DELAY, wRandRDebounceTimerFired, scr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* USE_RANDR */
|
||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
/* randr.h - RandR multi-monitor support
|
||||||
|
*
|
||||||
|
* Window Maker window manager
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 Window Maker Team
|
||||||
|
*
|
||||||
|
* 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _WMRANDR_H_
|
||||||
|
#define _WMRANDR_H_
|
||||||
|
|
||||||
|
#ifdef USE_RANDR
|
||||||
|
|
||||||
|
#include <X11/extensions/Xrandr.h>
|
||||||
|
#include "screen.h"
|
||||||
|
|
||||||
|
void wRandRInit(WScreen *scr);
|
||||||
|
void wRandRTeardown(WScreen *scr);
|
||||||
|
void wRandRHandleNotify(WScreen *scr, XEvent *event);
|
||||||
|
|
||||||
|
#endif /* USE_RANDR */
|
||||||
|
|
||||||
|
#endif /* _WMRANDR_H_ */
|
||||||
+19
-2
@@ -34,7 +34,7 @@
|
|||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#include <X11/XKBlib.h>
|
#include <X11/XKBlib.h>
|
||||||
#ifdef USE_RANDR
|
#ifdef USE_RANDR
|
||||||
#include <X11/extensions/Xrandr.h>
|
#include "randr.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <wraster.h>
|
#include <wraster.h>
|
||||||
@@ -642,6 +642,10 @@ WScreen *wScreenInit(int screen_number)
|
|||||||
scr->usableArea[i].y2 = scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
|
scr->usableArea[i].y2 = scr->totalUsableArea[i].y2 = rect.pos.y + rect.size.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_RANDR
|
||||||
|
wRandRInit(scr);
|
||||||
|
#endif
|
||||||
|
|
||||||
scr->fakeGroupLeaders = WMCreateArray(16);
|
scr->fakeGroupLeaders = WMCreateArray(16);
|
||||||
|
|
||||||
CantManageScreen = 0;
|
CantManageScreen = 0;
|
||||||
@@ -669,7 +673,10 @@ WScreen *wScreenInit(int screen_number)
|
|||||||
|
|
||||||
#ifdef USE_RANDR
|
#ifdef USE_RANDR
|
||||||
if (w_global.xext.randr.supported)
|
if (w_global.xext.randr.supported)
|
||||||
XRRSelectInput(dpy, scr->root_win, RRScreenChangeNotifyMask);
|
XRRSelectInput(dpy, scr->root_win,
|
||||||
|
RRScreenChangeNotifyMask |
|
||||||
|
RRCrtcChangeNotifyMask |
|
||||||
|
RROutputChangeNotifyMask);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
XSync(dpy, False);
|
XSync(dpy, False);
|
||||||
@@ -1367,3 +1374,13 @@ void ScreenCapture(WScreen *scr, int mode)
|
|||||||
wfree(filepath);
|
wfree(filepath);
|
||||||
wfree(screenshot_dir);
|
wfree(screenshot_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wScreenDestroy(WScreen *scr)
|
||||||
|
{
|
||||||
|
#ifdef USE_RANDR
|
||||||
|
wRandRTeardown(scr);
|
||||||
|
#else
|
||||||
|
/* Parameter not used, but tell the compiler that it is ok */
|
||||||
|
(void) scr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
+6
-1
@@ -299,6 +299,11 @@ typedef struct _WScreen {
|
|||||||
/* for hot-corners delay */
|
/* for hot-corners delay */
|
||||||
WMHandlerID *hot_corner_timer;
|
WMHandlerID *hot_corner_timer;
|
||||||
|
|
||||||
|
#ifdef USE_RANDR
|
||||||
|
WMHandlerID *randr_debounce_timer;
|
||||||
|
void *randr_state;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* for window shortcuts */
|
/* for window shortcuts */
|
||||||
WMArray *shortcutWindows[MAX_WINDOW_SHORTCUTS];
|
WMArray *shortcutWindows[MAX_WINDOW_SHORTCUTS];
|
||||||
|
|
||||||
@@ -345,7 +350,7 @@ WScreen *wScreenWithNumber(int i);
|
|||||||
WScreen *wScreenForRootWindow(Window window); /* window must be valid */
|
WScreen *wScreenForRootWindow(Window window); /* window must be valid */
|
||||||
WScreen *wScreenForWindow(Window window); /* slower than above functions */
|
WScreen *wScreenForWindow(Window window); /* slower than above functions */
|
||||||
|
|
||||||
void wScreenFinish(WScreen *scr);
|
void wScreenDestroy(WScreen *scr);
|
||||||
void wScreenUpdateUsableArea(WScreen *scr);
|
void wScreenUpdateUsableArea(WScreen *scr);
|
||||||
|
|
||||||
void create_logo_image(WScreen *scr);
|
void create_logo_image(WScreen *scr);
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include "winspector.h"
|
#include "winspector.h"
|
||||||
#include "wmspec.h"
|
#include "wmspec.h"
|
||||||
#include "colormap.h"
|
#include "colormap.h"
|
||||||
|
#include "screen.h"
|
||||||
#include "shutdown.h"
|
#include "shutdown.h"
|
||||||
|
|
||||||
|
|
||||||
@@ -81,6 +82,7 @@ void Shutdown(WShutdownMode mode)
|
|||||||
wipeDesktop(scr);
|
wipeDesktop(scr);
|
||||||
else
|
else
|
||||||
RestoreDesktop(scr);
|
RestoreDesktop(scr);
|
||||||
|
wScreenDestroy(scr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExecExitScript();
|
ExecExitScript();
|
||||||
@@ -103,6 +105,7 @@ void Shutdown(WShutdownMode mode)
|
|||||||
kill(scr->helper_pid, SIGKILL);
|
kill(scr->helper_pid, SIGKILL);
|
||||||
wScreenSaveState(scr);
|
wScreenSaveState(scr);
|
||||||
RestoreDesktop(scr);
|
RestoreDesktop(scr);
|
||||||
|
wScreenDestroy(scr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
+17
-1
@@ -600,7 +600,23 @@ void StartUp(Bool defaultScreenOnly)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_RANDR
|
#ifdef USE_RANDR
|
||||||
w_global.xext.randr.supported = XRRQueryExtension(dpy, &w_global.xext.randr.event_base, &j);
|
{
|
||||||
|
int rr_major = 0, rr_minor = 0;
|
||||||
|
Bool rr_ext = XRRQueryExtension(dpy, &w_global.xext.randr.event_base, &j);
|
||||||
|
Bool rr_ver = rr_ext && XRRQueryVersion(dpy, &rr_major, &rr_minor);
|
||||||
|
|
||||||
|
if (rr_ver && (rr_major > 1 || (rr_major == 1 && rr_minor >= 3))) {
|
||||||
|
w_global.xext.randr.supported = 1;
|
||||||
|
} else {
|
||||||
|
w_global.xext.randr.supported = 0;
|
||||||
|
if (!rr_ext)
|
||||||
|
wwarning(_("RandR extension is not available"));
|
||||||
|
else if (!rr_ver)
|
||||||
|
wwarning(_("RandR version check failed, RandR disabled"));
|
||||||
|
else
|
||||||
|
wwarning(_("RandR version %d.%d found but RandR version >=1.3 required"), rr_major, rr_minor);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
w_global.xext.xkb.supported = XkbQueryExtension(dpy, NULL, &w_global.xext.xkb.event_base, NULL, NULL, NULL);
|
w_global.xext.xkb.supported = XkbQueryExtension(dpy, NULL, &w_global.xext.xkb.event_base, NULL, NULL, NULL);
|
||||||
|
|||||||
@@ -2294,6 +2294,26 @@ void wWindowMove(WWindow *wwin, int req_x, int req_y)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Move the window to the nearest on-screen position if its stored
|
||||||
|
* frame origin falls in dead space (for example when a RandR monitor
|
||||||
|
* was removed while the window was miniaturized or hidden) */
|
||||||
|
void wWindowSnapToHead(WWindow *wwin)
|
||||||
|
{
|
||||||
|
#ifdef USE_RANDR
|
||||||
|
int bw = HAS_BORDER(wwin) ? wwin->screen_ptr->frame_border_width : 0;
|
||||||
|
int rx = wwin->frame_x - bw;
|
||||||
|
int ry = wwin->frame_y - bw;
|
||||||
|
|
||||||
|
if (wScreenBringInside(wwin->screen_ptr, &rx, &ry,
|
||||||
|
wwin->frame->core->width + 2 * bw,
|
||||||
|
wwin->frame->core->height + 2 * bw))
|
||||||
|
wWindowMove(wwin, rx + bw, ry + bw);
|
||||||
|
#else
|
||||||
|
/* Parameter not used, but tell the compiler that it is ok */
|
||||||
|
(void) wwin;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
void wWindowUpdateButtonImages(WWindow *wwin)
|
void wWindowUpdateButtonImages(WWindow *wwin)
|
||||||
{
|
{
|
||||||
WScreen *scr = wwin->screen_ptr;
|
WScreen *scr = wwin->screen_ptr;
|
||||||
|
|||||||
@@ -362,6 +362,7 @@ void wWindowConfigure(WWindow *wwin, int req_x, int req_y,
|
|||||||
int req_width, int req_height);
|
int req_width, int req_height);
|
||||||
|
|
||||||
void wWindowMove(WWindow *wwin, int req_x, int req_y);
|
void wWindowMove(WWindow *wwin, int req_x, int req_y);
|
||||||
|
void wWindowSnapToHead(WWindow *wwin);
|
||||||
|
|
||||||
void wWindowSynthConfigureNotify(WWindow *wwin);
|
void wWindowSynthConfigureNotify(WWindow *wwin);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user