On Sun, Feb 04, 2024 at 06:29:40PM +0100, Страхиња Радић wrote:
> 
> That is how it usually works. Users submit patches, which are provided on
> 
>       https://st.suckless.org/patches/
> 
> and (depending on context) might get merged into the upstream. This 
> particular 
> patch doesn't look like it should be in the upstream, but might be listed on 
> the above URL. In any case, you should format it according to
> 
>       https://suckless.org/hacking/

Alright, as I have pointed out earlier, these patches are NOT ready for
inclusion even in the wiki and will almost certainly have to be redone
anyway.
The first patch by Jochen and my second patch should eventually be squashed.

Still, here are the patch files. Even with the required file names, that do
not allow you to represent properly which one comes first and which one
later. But I am sure somebody will complain about the names, if I didn't do it.
>From cc593e750726df59f7af11a3a5596dd3d8b67e33 Mon Sep 17 00:00:00 2001
From: Jochen Sprickerhof <suckl...@jochen.sprickerhof.de>
Date: Thu, 1 Feb 2024 06:16:04 +0300
Subject: [PATCH 1/3] Hi list,

I've implemented embedder support, i.e. the host part, for st. This
allows clients to embed into the st window and is useful if you start X
applications from the terminal. For example:

$ surf -e $WINDOWID

The behavior is similar to Plan 9 where applications can take over
windows.. as far as this is possible in X ;).

The attached patch is against git master and I intent to put it into the
wiki unless someone things it's worth for mainline.

Cheers Jochen

Source: https://lists.suckless.org/hackers/2001/17072.html
---
 x.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 81 insertions(+), 1 deletion(-)

diff --git a/x.c b/x.c
index b36fb8c..aaa8139 100644
--- a/x.c
+++ b/x.c
@@ -66,6 +66,10 @@ static void ttysend(const Arg *);
 /* XEMBED messages */
 #define XEMBED_FOCUS_IN  4
 #define XEMBED_FOCUS_OUT 5
+#define XEMBED_EMBEDDED_NOTIFY 0
+#define XEMBED_WINDOW_ACTIVATE 1
+#define XEMBED_FOCUS_CURRENT   0
+
 
 /* macros */
 #define IS_SET(flag)		((win.mode & (flag)) != 0)
@@ -185,6 +189,9 @@ static void mousesel(XEvent *, int);
 static void mousereport(XEvent *);
 static char *kmap(KeySym, uint);
 static int match(uint, uint);
+static void createnotify(XEvent *e);
+static void destroynotify(XEvent *e);
+static void sendxembed(long msg, long detail, long d1, long d2);
 
 static void run(void);
 static void usage(void);
@@ -213,6 +220,8 @@ static void (*handler[LASTEvent])(XEvent *) = {
  */
 	[PropertyNotify] = propnotify,
 	[SelectionRequest] = selrequest,
+	[CreateNotify] = createnotify,
+	[DestroyNotify] = destroynotify,
 };
 
 /* Globals */
@@ -220,6 +229,7 @@ static DC dc;
 static XWindow xw;
 static XSelection xsel;
 static TermWindow win;
+static Window embed;
 
 /* Font Ring Cache */
 enum {
@@ -744,6 +754,55 @@ cresize(int width, int height)
 	ttyresize(win.tw, win.th);
 }
 
+void
+createnotify(XEvent *e)
+{
+	XWindowChanges wc;
+
+	if (embed || e->xcreatewindow.override_redirect)
+		return;
+
+	embed = e->xcreatewindow.window;
+
+	XReparentWindow(xw.dpy, embed, xw.win, 0, 0);
+	XSelectInput(xw.dpy, embed, PropertyChangeMask | StructureNotifyMask | EnterWindowMask);
+
+	XMapWindow(xw.dpy, embed);
+	sendxembed(XEMBED_EMBEDDED_NOTIFY, 0, xw.win, 0);
+
+	wc.width = win.w;
+	wc.height = win.h;
+	XConfigureWindow(xw.dpy, embed, CWWidth | CWHeight, &wc);
+
+	XSetInputFocus(xw.dpy, embed, RevertToParent, CurrentTime);
+}
+
+void
+destroynotify(XEvent *e)
+{
+	visibility(e);
+	if (embed == e->xdestroywindow.window) {
+		focus(e);
+	}
+}
+
+void
+sendxembed(long msg, long detail, long d1, long d2)
+{
+	XEvent e = { 0 };
+
+	e.xclient.window = embed;
+	e.xclient.type = ClientMessage;
+	e.xclient.message_type = xw.xembed;
+	e.xclient.format = 32;
+	e.xclient.data.l[0] = CurrentTime;
+	e.xclient.data.l[1] = msg;
+	e.xclient.data.l[2] = detail;
+	e.xclient.data.l[3] = d1;
+	e.xclient.data.l[4] = d2;
+	XSendEvent(xw.dpy, embed, False, NoEventMask, &e);
+}
+
 void
 xresize(int col, int row)
 {
@@ -1165,7 +1224,8 @@ xinit(int cols, int rows)
 	xw.attrs.bit_gravity = NorthWestGravity;
 	xw.attrs.event_mask = FocusChangeMask | KeyPressMask | KeyReleaseMask
 		| ExposureMask | VisibilityChangeMask | StructureNotifyMask
-		| ButtonMotionMask | ButtonPressMask | ButtonReleaseMask;
+		| ButtonMotionMask | ButtonPressMask | ButtonReleaseMask
+		| SubstructureNotifyMask | SubstructureRedirectMask;
 	xw.attrs.colormap = xw.cmap;
 
 	if (!(opt_embed && (parent = strtol(opt_embed, NULL, 0))))
@@ -1715,6 +1775,11 @@ visibility(XEvent *ev)
 void
 unmap(XEvent *ev)
 {
+	if (embed == ev->xunmap.window) {
+		embed = 0;
+		XRaiseWindow(xw.dpy, xw.win);
+		XSetInputFocus(xw.dpy, xw.win, RevertToParent, CurrentTime);
+	}
 	win.mode &= ~MODE_VISIBLE;
 }
 
@@ -1767,6 +1832,13 @@ focus(XEvent *ev)
 {
 	XFocusChangeEvent *e = &ev->xfocus;
 
+	if (embed && ev->type == FocusIn) {
+		XRaiseWindow(xw.dpy, embed);
+		XSetInputFocus(xw.dpy, embed, RevertToParent, CurrentTime);
+		sendxembed(XEMBED_FOCUS_IN, XEMBED_FOCUS_CURRENT, 0, 0);
+		sendxembed(XEMBED_WINDOW_ACTIVATE, 0, 0, 0);
+	}
+
 	if (e->mode == NotifyGrab)
 		return;
 
@@ -1905,9 +1977,17 @@ cmessage(XEvent *e)
 void
 resize(XEvent *e)
 {
+	XWindowChanges wc;
+
 	if (e->xconfigure.width == win.w && e->xconfigure.height == win.h)
 		return;
 
+	if (embed) {
+		wc.width = e->xconfigure.width;
+		wc.height = e->xconfigure.height;
+		XConfigureWindow(xw.dpy, embed, CWWidth | CWHeight, &wc);
+	}
+
 	cresize(e->xconfigure.width, e->xconfigure.height);
 }
 
-- 
2.43.0

>From 4de1ba2a851515da01000f8ed7406d4669bdecc3 Mon Sep 17 00:00:00 2001
From: Robin Haberkorn <robin.haberk...@googlemail.com>
Date: Thu, 1 Feb 2024 17:11:48 +0300
Subject: [PATCH 2/3] Embedder: This respects the embedded window's name/title

Imports gettextprop() from tabbed.c.
---
 x.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/x.c b/x.c
index aaa8139..6b3a8e1 100644
--- a/x.c
+++ b/x.c
@@ -192,6 +192,8 @@ static int match(uint, uint);
 static void createnotify(XEvent *e);
 static void destroynotify(XEvent *e);
 static void sendxembed(long msg, long detail, long d1, long d2);
+static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
+static void updatetitle(Window win);
 
 static void run(void);
 static void usage(void);
@@ -524,6 +526,9 @@ propnotify(XEvent *e)
 			(xpev->atom == XA_PRIMARY ||
 			 xpev->atom == clipboard)) {
 		selnotify(e);
+	} else if (xpev->state != PropertyDelete && xpev->atom == XA_WM_NAME &&
+	           embed == xpev->window) {
+		updatetitle(embed);
 	}
 }
 
@@ -767,6 +772,8 @@ createnotify(XEvent *e)
 	XReparentWindow(xw.dpy, embed, xw.win, 0, 0);
 	XSelectInput(xw.dpy, embed, PropertyChangeMask | StructureNotifyMask | EnterWindowMask);
 
+	updatetitle(embed);
+
 	XMapWindow(xw.dpy, embed);
 	sendxembed(XEMBED_EMBEDDED_NOTIFY, 0, xw.win, 0);
 
@@ -803,6 +810,44 @@ sendxembed(long msg, long detail, long d1, long d2)
 	XSendEvent(xw.dpy, embed, False, NoEventMask, &e);
 }
 
+Bool
+gettextprop(Window w, Atom atom, char *text, unsigned int size)
+{
+	char **list = NULL;
+	int n;
+	XTextProperty name;
+
+	if (!text || size == 0)
+		return False;
+
+	text[0] = '\0';
+	XGetTextProperty(xw.dpy, w, &name, atom);
+	if (!name.nitems)
+		return False;
+
+	if (name.encoding == XA_STRING) {
+		strncpy(text, (char *)name.value, size - 1);
+	} else if (XmbTextPropertyToTextList(xw.dpy, &name, &list, &n) >= Success
+	           && n > 0 && *list) {
+		strncpy(text, *list, size - 1);
+		XFreeStringList(list);
+	}
+	text[size - 1] = '\0';
+	XFree(name.value);
+
+	return True;
+}
+
+void
+updatetitle(Window win)
+{
+	char name[256];
+
+	if (!gettextprop(win, xw.netwmname, name, sizeof(name)))
+		gettextprop(win, XA_WM_NAME, name, sizeof(name));
+	xsettitle(name);
+}
+
 void
 xresize(int col, int row)
 {
-- 
2.43.0

>From 73028d36e877c3c918ba96ffc95a3cc2a1b710b2 Mon Sep 17 00:00:00 2001
From: Robin Haberkorn <robin.haberk...@googlemail.com>
Date: Sat, 3 Feb 2024 03:36:47 +0300
Subject: [PATCH 3/3] inherit window icon from embedded (Xembed) application

* This is largely taken from tabbed's "icon" patch (cf. xseticon()):
  https://tools.suckless.org/tabbed/patches/icon/
* I found that some applications that show their icon flawlessly in the WM
  won't work (neither in st, nor in tabbed).
  This was the case with SciTECO, but it could be resolved
  by reordering some statements.
* Once the embedded application terminates, we try to remove the icon again.
  `xprop` confirms that this worked.
  Unfortunately, Awesome WM will continue to show the old icon, which
  is probably an Awesome WM bug.
  Perhaps we should therefore apply this patch on top of netwmicon (FIXME):
  https://st.suckless.org/patches/netwmicon/
---
 x.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/x.c b/x.c
index 6b3a8e1..66dccc4 100644
--- a/x.c
+++ b/x.c
@@ -194,6 +194,7 @@ static void destroynotify(XEvent *e);
 static void sendxembed(long msg, long detail, long d1, long d2);
 static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size);
 static void updatetitle(Window win);
+static void updateicon(Window win);
 
 static void run(void);
 static void usage(void);
@@ -520,6 +521,7 @@ propnotify(XEvent *e)
 {
 	XPropertyEvent *xpev;
 	Atom clipboard = XInternAtom(xw.dpy, "CLIPBOARD", 0);
+	Atom wmicon = XInternAtom(xw.dpy, "_NET_WM_ICON", 0);
 
 	xpev = &e->xproperty;
 	if (xpev->state == PropertyNewValue &&
@@ -529,6 +531,9 @@ propnotify(XEvent *e)
 	} else if (xpev->state != PropertyDelete && xpev->atom == XA_WM_NAME &&
 	           embed == xpev->window) {
 		updatetitle(embed);
+	} else if ((xpev->atom == wmicon || xpev->state == PropertyNewValue && xpev->atom == XA_WM_HINTS) &&
+	           embed == xpev->window) {
+		updateicon(embed);
 	}
 }
 
@@ -773,6 +778,7 @@ createnotify(XEvent *e)
 	XSelectInput(xw.dpy, embed, PropertyChangeMask | StructureNotifyMask | EnterWindowMask);
 
 	updatetitle(embed);
+	updateicon(embed);
 
 	XMapWindow(xw.dpy, embed);
 	sendxembed(XEMBED_EMBEDDED_NOTIFY, 0, xw.win, 0);
@@ -848,6 +854,46 @@ updatetitle(Window win)
 	xsettitle(name);
 }
 
+void
+updateicon(Window win)
+{
+	Atom ret_type;
+	XWMHints *wmh, *cwmh;
+	int ret_format;
+	unsigned long ret_nitems, ret_nleft;
+	long offset = 0L;
+	unsigned char *data = NULL;
+
+	Atom wmicon = XInternAtom(xw.dpy, "_NET_WM_ICON", 0);
+	XDeleteProperty(xw.dpy, xw.win, wmicon);
+
+	wmh = XGetWMHints(xw.dpy, xw.win);
+	wmh->flags &= ~(IconPixmapHint | IconMaskHint);
+	wmh->icon_pixmap = wmh->icon_mask = None;
+
+	if (win) {
+		if (XGetWindowProperty(xw.dpy, win, wmicon, offset, LONG_MAX, False,
+		                       XA_CARDINAL, &ret_type, &ret_format, &ret_nitems,
+		                       &ret_nleft, &data) == Success &&
+		    ret_type == XA_CARDINAL && ret_format == 32) {
+			XChangeProperty(xw.dpy, xw.win, wmicon, XA_CARDINAL, 32,
+			                PropModeReplace, data, ret_nitems);
+		} else if ((cwmh = XGetWMHints(xw.dpy, win)) && cwmh->flags & IconPixmapHint) {
+			wmh->flags |= IconPixmapHint;
+			wmh->icon_pixmap = cwmh->icon_pixmap;
+			if (cwmh->flags & IconMaskHint) {
+				wmh->flags |= IconMaskHint;
+				wmh->icon_mask = cwmh->icon_mask;
+			}
+			XFree(cwmh);
+		}
+	}
+
+	XSetWMHints(xw.dpy, xw.win, wmh);
+	XFree(wmh);
+	XFree(data);
+}
+
 void
 xresize(int col, int row)
 {
@@ -1821,6 +1867,9 @@ void
 unmap(XEvent *ev)
 {
 	if (embed == ev->xunmap.window) {
+		/* FIXME: restore the previous window title? */
+		/* FIXME: Does not remove the icon */
+		updateicon(0);
 		embed = 0;
 		XRaiseWindow(xw.dpy, xw.win);
 		XSetInputFocus(xw.dpy, xw.win, RevertToParent, CurrentTime);
-- 
2.43.0

Reply via email to