Skip to content

Commit a7094de

Browse files
authored
Merge pull request #65 from gnustep/startupid
Attempt to fix #62
2 parents 3665c40 + 076f0db commit a7094de

File tree

3 files changed

+125
-16
lines changed

3 files changed

+125
-16
lines changed

ChangeLog

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
2025-12-18 Richard Frith-Macdonald <rfm@gnu.org>
2+
3+
* Source/x11/XGServer.m:
4+
* Source/x11/XGServerWindow.m:
5+
If DESKTOP_STARTUP_ID environment variable is present, set it as the
6+
_NET_STARTUP_ID property of the first window we make visible (to tell
7+
the WM we are starting up) and, when launch completes, send a message
8+
to the root window to tell it that startup is over.
9+
This is an attempt to fix issue #62 on github by implementing the
10+
XDG startup notification mechanism.
11+
112
2025-05-21 Fred Kiefer <FredKiefer@gmx.de>
213

314
* Source/x11/scale.c,

Source/x11/XGServer.m

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,48 @@ + (Display *) xDisplay
382382
return [(XGServer*)GSCurrentServer() xDisplay];
383383
}
384384

385+
static NSString *startupID = nil;
386+
387+
- (void) _didFinishLaunching: (NSNotification*)n
388+
{
389+
/* To tell the window manager that we have completed launching we must
390+
* send a _NET_STARTUP_INFO_END message telling it that startup has ended
391+
* for the DESKTOP_STARTUP_ID (issue #62 on github)
392+
*/
393+
if (startupID)
394+
{
395+
const char *startup_id = [startupID UTF8String];
396+
Window root = RootWindow(dpy, defScreen);
397+
Atom atom = XInternAtom(dpy, "_NET_STARTUP_INFO_END", False);
398+
int len = strlen(startup_id);
399+
char *msg = malloc(len + 12);
400+
int pos = 0;
401+
402+
snprintf(msg, len+12, "remove: ID=%s", startup_id);
403+
len = strlen(msg) + 1;
404+
405+
while (pos < len)
406+
{
407+
int chunk;
408+
XEvent ev = {0};
409+
410+
ev.xclient.type = ClientMessage;
411+
ev.xclient.window = root;
412+
ev.xclient.message_type = atom;
413+
ev.xclient.format = 8;
414+
415+
chunk = (len - pos > 20) ? 20 : (len - pos);
416+
memcpy(ev.xclient.data.b, msg + pos, chunk);
417+
418+
XSendEvent(dpy, root, False, PropertyChangeMask, &ev);
419+
pos += chunk;
420+
}
421+
XFlush(dpy);
422+
423+
DESTROY(startupID);
424+
}
425+
}
426+
385427
- (id) _initXContext
386428
{
387429
int screen_id, display_id;
@@ -490,6 +532,17 @@ - (id) initWithAttributes: (NSDictionary *)info
490532
[super initWithAttributes: info];
491533
[self _initXContext];
492534

535+
ASSIGN(startupID, [[[NSProcessInfo processInfo] environment]
536+
objectForKey: @"DESKTOP_STARTUP_ID"]);
537+
if (startupID)
538+
{
539+
[[NSNotificationCenter defaultCenter]
540+
addObserver: self
541+
selector: @selector(_didFinishLaunching:)
542+
name: NSApplicationDidFinishLaunchingNotification
543+
object: nil];
544+
}
545+
493546
[self setupRunLoopInputSourcesForMode: NSDefaultRunLoopMode];
494547
[self setupRunLoopInputSourcesForMode: NSConnectionReplyMode];
495548
[self setupRunLoopInputSourcesForMode: NSModalPanelRunLoopMode];

Source/x11/XGServerWindow.m

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
static NSMapTable *windowtags = NULL;
9090

9191
/* Track used window numbers */
92-
static int last_win_num = 0;
92+
static int last_win_num = 0;
9393

9494
@interface NSCursor (BackendPrivate)
9595
- (void *)_cid;
@@ -1439,14 +1439,16 @@ - (void) _setupRootWindow
14391439
* Set up standard atoms.
14401440
*/
14411441
#ifdef HAVE_XINTERNATOMS
1442-
XInternAtoms(dpy, atom_names, sizeof(atom_names)/sizeof(char*),
1443-
False, generic.atoms);
1442+
XInternAtoms(dpy, atom_names, sizeof(atom_names)/sizeof(char*),
1443+
False, generic.atoms);
14441444
#else
1445-
{
1446-
int atomCount;
1445+
{
1446+
int count;
14471447

1448-
for (atomCount = 0; atomCount < sizeof(atom_names)/sizeof(char*); atomCount++)
1449-
generic.atoms[atomCount] = XInternAtom(dpy, atom_names[atomCount], False);
1448+
for (count = 0; count < sizeof(atom_names)/sizeof(char*); count++)
1449+
{
1450+
generic.atoms[count] = XInternAtom(dpy, atom_names[count], False);
1451+
}
14501452
}
14511453
#endif
14521454

@@ -1580,9 +1582,9 @@ - (void) _setupRootWindow
15801582
* hold 64bit values.
15811583
*/
15821584
XChangeProperty(dpy, ROOT,
1583-
generic._GNUSTEP_WM_ATTR_ATOM, generic._GNUSTEP_WM_ATTR_ATOM,
1584-
32, PropModeReplace, (unsigned char *)&win_attrs,
1585-
sizeof(GNUstepWMAttributes)/sizeof(CARD32));
1585+
generic._GNUSTEP_WM_ATTR_ATOM, generic._GNUSTEP_WM_ATTR_ATOM,
1586+
32, PropModeReplace, (unsigned char *)&win_attrs,
1587+
sizeof(GNUstepWMAttributes)/sizeof(CARD32));
15861588
}
15871589

15881590
if ((generic.wm & XGWM_EWMH) != 0)
@@ -1595,12 +1597,13 @@ - (void) _setupRootWindow
15951597
* hold 64bit values.
15961598
*/
15971599
XChangeProperty(dpy, ROOT,
1598-
generic._NET_WM_PID_ATOM, XA_CARDINAL,
1599-
32, PropModeReplace,
1600-
(unsigned char*)&pid, 1);
1600+
generic._NET_WM_PID_ATOM, XA_CARDINAL,
1601+
32, PropModeReplace,
1602+
(unsigned char*)&pid, 1);
16011603
// FIXME: Need to set WM_CLIENT_MACHINE as well.
16021604
}
16031605

1606+
16041607
/* We need to determine the offsets between the actual decorated window
16051608
* and the window we draw into.
16061609
*/
@@ -1632,9 +1635,9 @@ - (void) _setupRootWindow
16321635
}
16331636
else
16341637
{
1635-
offsets = (uint16_t *)PropGetCheckProperty(dpy, DefaultRootWindow(dpy),
1636-
generic._GNUSTEP_FRAME_OFFSETS_ATOM,
1637-
XA_CARDINAL, 16, 60, &count);
1638+
offsets = (uint16_t *)PropGetCheckProperty(dpy,
1639+
DefaultRootWindow(dpy), generic._GNUSTEP_FRAME_OFFSETS_ATOM,
1640+
XA_CARDINAL, 16, 60, &count);
16381641
}
16391642

16401643
if (offsets == 0)
@@ -1924,6 +1927,7 @@ - (int) window: (NSRect)frame : (NSBackingStoreType)type : (unsigned int)style
19241927
* It could be done for popup windows, but at this point we don't know
19251928
* about the usage of the window.
19261929
*/
1930+
19271931
window->xwn_attrs.override_redirect = False;
19281932

19291933
window->ident = XCreateWindow(dpy, window->root,
@@ -2870,6 +2874,47 @@ - (void) orderwindow: (int)op : (int)otherWin : (int)winNum
28702874

28712875
if (op != NSWindowOut)
28722876
{
2877+
static BOOL beenHere = NO;
2878+
2879+
/* To tell the window manager that our window corresponds to
2880+
* a particular task we must pass it the DESKTOP_STARTUP_ID
2881+
* by setting it as a property of the first toplevel window
2882+
* made visible.
2883+
* (issue #62 on github)
2884+
*/
2885+
if (NO == beenHere)
2886+
{
2887+
NSProcessInfo *p = [NSProcessInfo processInfo];
2888+
NSDictionary *e = [p environment];
2889+
NSString *s = [e objectForKey: @"DESKTOP_STARTUP_ID"];
2890+
2891+
beenHere = YES;
2892+
if ([s length] > 0)
2893+
{
2894+
const char *ptr = [s UTF8String];
2895+
2896+
XChangeProperty(dpy, window->ident,
2897+
XInternAtom(dpy, "_NET_STARTUP_ID", False),
2898+
generic.UTF8_STRING_ATOM,
2899+
8,
2900+
PropModeReplace,
2901+
(unsigned char *)ptr,
2902+
strlen(ptr)
2903+
);
2904+
/* Ideally we should remove the value from the environment to
2905+
* prevent any other library from acting on it. There is no
2906+
* OpenStep/Cocoa APIU to do that, but we can use an extension
2907+
* from GNUstepBase if available.
2908+
*/
2909+
if ([p respondsToSelector: @selector(setValue:inEnvironment:)])
2910+
{
2911+
[p performSelector: @selector(setValue:inEnvironment:)
2912+
withObject: nil
2913+
withObject: @"DESKTOP_STARTUP_ID"];
2914+
}
2915+
}
2916+
}
2917+
28732918
/*
28742919
* Some window managers ignore any hints and properties until the
28752920
* window is actually mapped, so we need to set them all up

0 commit comments

Comments
 (0)