1
0
mirror of https://github.com/gryf/wmaker.git synced 2026-07-07 19:26:39 +02:00

Compare commits

...

9 Commits

Author SHA1 Message Date
David Maciejak 774e696270 wmsetbg: free the previous root pixmap via the ESETROOT pairing
Commit 6c107e0c switched duplicatePixmap() to RetainTemporary and
removed XKillClient() from setPixmapProperty(). RetainTemporary
resources are released only on server reset, so each background change
now leaves a full-screen pixmap retained in the X server until logout.
In helper mode that is one leak per workspace switch.
2026-06-18 18:07:55 +01:00
Haroldo Santos 6c107e0ce5 wmsetbg: fix "X connection broken" crash when setting background
setPixmapProperty() called XKillClient(dpy, old_pixmap) to free the
previous background pixmap stored in _XROOTPMAP_ID.  The pixmap was
created by duplicatePixmap() via a short-lived second X connection
that used XSetCloseDownMode(RetainPermanent).  After that connection
closed, the X server eventually recycled its client ID for a new
connection.  If wmsetbg's own 'dpy' connection received that recycled
ID, XKillClient() would kill wmsetbg itself, causing the fatal error:

  X connection to :0 broken (explicit kill or server shutdown).

Fix by switching from RetainPermanent to RetainTemporary in
duplicatePixmap().  With RetainTemporary, background pixmaps are kept
alive for the lifetime of the X session and freed automatically at
logout — no explicit XKillClient() is needed.

Remove the now-dead XKillClient() block from setPixmapProperty():
- dummyErrorHandler() was only used to suppress its X errors; remove.
- The mode variable toggled PropModeReplace/PropModeAppend based on
  whether an old pixmap was present; since we always want to replace,
  use PropModeReplace unconditionally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 17:29:07 +01:00
Haroldo Santos 7c80e917d7 wmsetbg: fix memory leak of XGetWindowProperty data in setPixmapProperty
XGetWindowProperty() allocates a buffer and returns it via the 'data'
pointer; the caller is responsible for freeing it with XFree() when
done.  setPixmapProperty() read the old _XROOTPMAP_ID value but never
freed the returned buffer, leaking memory on every background change.

Add the missing XFree(data) call.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-15 17:29:07 +01:00
David Maciejak 90ce10998c wmaker: ignore queued motion before the screenshot anchor click
imageCaptureArea() consumed any MotionNotify still queued from the
root window before Button1 set the anchor point, with xp,yp still -1
the rectangle math anchored the XOR wireframe at (-1,-1) and drew a
box from the top-left corner to the pointer. Drop motion events
until the press has recorded an anchor.
2026-06-13 21:57:23 +01:00
David Maciejak f18de447f6 wmaker: grab the keyboard during partial screenshot selection
imageCaptureArea() already looked for XK_Escape in its event loop but
only grabbed the pointer, so the KeyPress went to the focused client
and the cancel branch was unreachable. Grab the keyboard for the
duration of the rubber-band selection, erase the XOR rectangle when
Escape aborts mid-drag, and release the keyboard on every exit path.
2026-06-13 21:57:23 +01:00
Hugo Carvalho b69c9db245 po: add European Portuguese translation, rename old pt to pt_BR
The existing pt.po was Brazilian Portuguese, so move it to pt_BR.po
unchanged and add a new European Portuguese pt.po. Register pt_BR in
EXTRA_DIST and update the maintainer table in po/README.

Imported from window-maker/wmaker pull request 68.
2026-06-13 21:55:08 +01:00
David Maciejak 3562ce9d7b wmaker: restore atom indices broken by _WINDOWMAKER_MARK_KEY insertion
The vim-like window marking change inserted "_WINDOWMAKER_MARK_KEY" at
atomNames[17] and appended wmaker.mark_key = atom[22], but did not shift
the existing atom[17..21] assignments. Since then gnustep.wm_attr,
gnustep.wm_miniaturize_window, gnustep.titlebar_state,
desktop.gtk_object_path, wm.ignore_focus_events and wmaker.mark_key have
each held the atom for the next name in the table, breaking GNUstep
window-level/titlebar/miniaturize handling, the GTK object-path skip,
WM_IGNORE_FOCUS_EVENTS and the mark feature itself.

Move the new name to the end of atomNames[] so positions 17..22 line up
with their assignments again.
2026-06-11 15:45:55 +01:00
David Maciejak aa7287efbf wmaker: check RCreateImage() result for _NET_WM_ICON
makeRImageFromARGBData() dereferences image->data immediately after
RCreateImage() without checking for NULL. A client that advertises a
20000x20000 icon makes RCreateImage() try a 1.6 GB malloc, on failure
wmaker segfaults.
2026-05-11 10:09:42 +01:00
David Maciejak 5eb3287535 wmaker: fix integer overflow in _NET_WM_ICON parser
findBestIcon() multiplies two attacker-controlled 32-bit ints (icon
width * height) without overflow checking. A client setting
_NET_WM_ICON = {2, 0x7FFFFFFF} makes "size" wrap to 0 so "i += size"
never advances and wmaker spins forever at 100% CPU.

The same loop also never verifies that the claimed icon actually fits
inside the property buffer, allowing a 2-element property to drive a
multi-KB OOB read in makeRImageFromARGBData().

Validate dimensions against a 4096-pixel cap (safe from unsigned long
overflow) and reject icons whose pixel data would extend past the end
of the property.
2026-05-11 10:09:42 +01:00
8 changed files with 2078 additions and 325 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ CLEANFILES = $(CATALOGS) $(DOMAIN).pot
# Please keep this sorted in alphabetic order!
EXTRA_DIST = be.po bg.po bs.po ca.po cs.po da.po de.po el.po es.po et.po \
fi.po fr.po fy.po gl.po hr.po hu.po hy.po it.po ja.po ko.po ms.po nl.po \
no.po pl.po pt.po ro.po ru.po sk.po sr.po sv.po tr.po uk.po zh_CN.po \
no.po pl.po pt.po pt_BR.po ro.po ru.po sk.po sr.po sv.po tr.po uk.po zh_CN.po \
zh_TW.po
POTFILES = \
+2 -1
View File
@@ -3,7 +3,8 @@ File Language Note Current Maintainer
ja.po Japanese 1 ABE Shige <sabe@ibm.net>
MANOME Tomonori <manome@itlb.te.noda.sut.ac.jp>
Takeo Hashimoto <HashimotoTakeo@mac.com>
pt.po Portuguese Eliphas Levy Theodoro <eliphas@conectiva.com.br>
pt_BR.po Brazilian Portuguese Eliphas Levy Theodoro <eliphas@conectiva.com.br>
pt.po Portuguese Hugo Carvalho <hugokarvalho@hotmail.com>
de.po German Torsten Marek <torsten.marek@vbdnet.de>
Bernd Eggert <Bernd.Eggert@t-online.de>
Matthias Warkus <mawarkus@t-online.de>
+228 -300
View File
File diff suppressed because it is too large Load Diff
+1776
View File
File diff suppressed because it is too large Load Diff
+11 -1
View File
@@ -1143,6 +1143,7 @@ static XImage *imageCaptureArea(WScreen *scr)
GrabModeAsync, None, wPreferences.cursor[WCUR_CAPTURE], CurrentTime) != Success) {
return NULL;
}
XGrabKeyboard(dpy, scr->root_win, False, GrabModeAsync, GrabModeAsync, CurrentTime);
XGrabServer(dpy);
@@ -1162,12 +1163,17 @@ static XImage *imageCaptureArea(WScreen *scr)
if (w > 0 && h > 0) {
XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
XUngrabServer(dpy);
XUngrabKeyboard(dpy, CurrentTime);
XUngrabPointer(dpy, CurrentTime);
return XGetImage(dpy, scr->root_win, x, y, w, h, AllPlanes, ZPixmap);
}
}
break;
case MotionNotify:
/* drop stale root MotionNotify still queued from before
* the grab, otherwise the rectangle anchors at (-1,-1) */
if (xp < 0)
break;
XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
x = event.xmotion.x_root;
if (x < xp) {
@@ -1186,8 +1192,11 @@ static XImage *imageCaptureArea(WScreen *scr)
XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
break;
case KeyPress:
if (W_KeycodeToKeysym(dpy, event.xkey.keycode, 0) == XK_Escape)
if (W_KeycodeToKeysym(dpy, event.xkey.keycode, 0) == XK_Escape) {
if (w > 0 && h > 0)
XDrawRectangle(dpy, scr->root_win, scr->frame_gc, x, y, w, h);
quit = 1;
}
break;
default:
WMHandleEvent(&event);
@@ -1196,6 +1205,7 @@ static XImage *imageCaptureArea(WScreen *scr)
}
XUngrabServer(dpy);
XUngrabKeyboard(dpy, CurrentTime);
XUngrabPointer(dpy, CurrentTime);
return NULL;
}
+3 -2
View File
@@ -387,7 +387,6 @@ static char *atomNames[] = {
"_WINDOWMAKER_COMMAND",
"_WINDOWMAKER_ICON_SIZE",
"_WINDOWMAKER_ICON_TILE",
"_WINDOWMAKER_MARK_KEY",
GNUSTEP_WM_ATTR_NAME,
GNUSTEP_WM_MINIATURIZE_WINDOW,
@@ -395,7 +394,9 @@ static char *atomNames[] = {
"_GTK_APPLICATION_OBJECT_PATH",
"WM_IGNORE_FOCUS_EVENTS"
"WM_IGNORE_FOCUS_EVENTS",
"_WINDOWMAKER_MARK_KEY"
};
/*
+12 -4
View File
@@ -408,6 +408,8 @@ static RImage *makeRImageFromARGBData(unsigned long *data)
return NULL;
image = RCreateImage(width, height, True);
if (!image)
return NULL;
for (imgdata = image->data, i = 2; i < size + 2; i++, imgdata += 4) {
pixel = data[i];
@@ -458,9 +460,11 @@ static RImage *findBestIcon(unsigned long *data, unsigned long items)
/* get the current icon's size */
sx = (int)data[i];
sy = (int)data[i + 1];
if ((sx < 1) || (sy < 1))
if (sx < 1 || sy < 1 || sx > 4096 || sy > 4096)
break;
size = (unsigned long)sx * (unsigned long)sy + 2;
if ((unsigned long)size > items - i)
break;
size = sx * sy + 2;
/* check the size difference if it's not too large */
if ((sx <= wanted) && (sy <= wanted)) {
@@ -485,8 +489,12 @@ static RImage *findBestIcon(unsigned long *data, unsigned long items)
* small image by a small scale. */
largest = 0;
for (i = 0L; i < items - 1;) {
size = (int)data[i] * (int)data[i + 1];
if (size == 0)
sx = (int)data[i];
sy = (int)data[i + 1];
if (sx < 1 || sy < 1 || sx > 4096 || sy > 4096)
break;
size = (unsigned long)sx * (unsigned long)sy;
if ((unsigned long)size + 2 > items - i)
break;
if (size > largest) {
icon = &data[i];
+45 -16
View File
@@ -811,38 +811,67 @@ static int dummyErrorHandler(Display * dpy, XErrorEvent * err)
return 0;
}
static void setPixmapProperty(Pixmap pixmap)
static Bool getRootPixmapAtom(Atom prop, Pixmap *ret)
{
static Atom prop = 0;
Atom type;
int format;
unsigned long length, after;
unsigned char *data;
int mode;
unsigned char *data = NULL;
*ret = None;
if (XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
&type, &format, &length, &after, &data) != Success)
return False;
if (type == XA_PIXMAP && format == 32 && length == 1 && data) {
*ret = *(Pixmap *)data;
XFree(data);
return True;
}
if (data)
XFree(data);
return False;
}
static void setPixmapProperty(Pixmap pixmap)
{
static Atom prop = 0;
static Atom eprop = 0;
Pixmap old_root, old_eset;
Bool have_root, have_eset;
if (!prop) {
prop = XInternAtom(dpy, "_XROOTPMAP_ID", False);
eprop = XInternAtom(dpy, "ESETROOT_PMAP_ID", False);
}
XGrabServer(dpy);
/* Clear out the old pixmap */
XGetWindowProperty(dpy, root, prop, 0L, 1L, False, AnyPropertyType,
&type, &format, &length, &after, &data);
if ((type == XA_PIXMAP) && (format == 32) && (length == 1)) {
/* Free the previous background pixmap only when both _XROOTPMAP_ID
* and ESETROOT_PMAP_ID hold the same XID, that pairing marks a
* RetainPermanent zombie left by a compliant root setter so
* XKillClient on it cannot hit a live connection. A bare
* _XROOTPMAP_ID may come from a tool that closed in Destroy mode,
* in which case the client bits of the stored XID can already be
* recycled to a live client, including our own dpy, and killing
* it produces "X connection broken". */
have_root = getRootPixmapAtom(prop, &old_root);
have_eset = getRootPixmapAtom(eprop, &old_eset);
if (have_root && have_eset && old_root == old_eset && old_eset != None) {
XSetErrorHandler(dummyErrorHandler);
XKillClient(dpy, *((Pixmap *) data));
XKillClient(dpy, old_eset);
XSync(dpy, False);
XSetErrorHandler(NULL);
mode = PropModeReplace;
} else {
mode = PropModeAppend;
}
if (pixmap)
XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, mode, (unsigned char *)&pixmap, 1);
else
if (pixmap) {
XChangeProperty(dpy, root, prop, XA_PIXMAP, 32, PropModeReplace,
(unsigned char *)&pixmap, 1);
XChangeProperty(dpy, root, eprop, XA_PIXMAP, 32, PropModeReplace,
(unsigned char *)&pixmap, 1);
} else {
XDeleteProperty(dpy, root, prop);
XDeleteProperty(dpy, root, eprop);
}
XUngrabServer(dpy);
XFlush(dpy);