Good day.
Attached to this email is a patch to add UPnP support to dwm. You will
need libupnp for compiling it. This feature is quiet useless, but dwm
is too simple and I have the sexual interference to write a mass of use-
less code once in a while. At
http://www.r-36.net/ctrldwm-0.1.tar.gz
is the corresponding controlling client.
The usage is:
% ctrldwm status $somestatus
[Some seconds lag. It's SOAP dude!]
%
This sets the status line of dwm.
Have fun guys, while using usless code!
Sincerely,
Christoph
diff -r 74739798b0b2 Makefile
--- a/Makefile Fri Sep 1 13:31:59 2006
+++ b/Makefile Wed Sep 6 12:06:50 2006
@@ -3,8 +3,9 @@
include config.mk
-SRC = client.c draw.c event.c main.c tag.c util.c view.c
+SRC = client.c draw.c event.c main.c tag.c util.c view.c upnp.c
OBJ = ${SRC:.c=.o}
+UPNP = dwm-service.xml dwm.xml
all: options dwm
@@ -38,7 +39,7 @@
@echo creating dist tarball
@mkdir -p dwm-${VERSION}
@cp -R LICENSE Makefile README config.*.h config.mk \
- dwm.1 dwm.h ${SRC} dwm-${VERSION}
+ dwm.1 dwm.h ${SRC} ${UPNP} dwm-${VERSION}
@tar -cf dwm-${VERSION}.tar dwm-${VERSION}
@gzip dwm-${VERSION}.tar
@rm -rf dwm-${VERSION}
@@ -52,6 +53,9 @@
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed 's/VERSION/${VERSION}/g' < dwm.1 > ${DESTDIR}${MANPREFIX}/man1/dwm.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dwm.1
+ @echo installing UPnP files to /var/dwm/upnp
+ @mkdir -p /var/dwm/upnp
+ @cp ${UPNP} /var/dwm/upnp
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
diff -r 74739798b0b2 config.mk
--- a/config.mk Fri Sep 1 13:31:59 2006
+++ b/config.mk Wed Sep 6 12:06:50 2006
@@ -1,5 +1,5 @@
# dwm version
-VERSION = 1.3
+VERSION = upnp-1.3
# Customize below to fit your system
@@ -12,7 +12,7 @@
# includes and libs
INCS = -I. -I/usr/include -I${X11INC}
-LIBS = -L/usr/lib -lc -L${X11LIB} -lX11
+LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lupnp
# flags
CFLAGS = -Os ${INCS} -DVERSION=\"${VERSION}\"
diff -r 74739798b0b2 dwm.h
--- a/dwm.h Fri Sep 1 13:31:59 2006
+++ b/dwm.h Wed Sep 6 12:06:50 2006
@@ -69,6 +69,7 @@
extern const char *tags[];
extern char stext[1024];
extern int bx, by, bw, bh, bmw, mw, screen, sx, sy, sw, sh;
+extern int doevent, doingevent;
extern unsigned int ntags, numlockmask;
extern void (*handler[LASTEvent])(XEvent *);
extern void (*arrange)(Arg *);
@@ -139,3 +140,8 @@
extern void view(Arg *arg);
extern void viewall(Arg *arg);
extern void zoom(Arg *arg);
+
+/* upnp.c */
+extern void startupnp(void);
+extern void endupnp(void);
+
diff -r 74739798b0b2 main.c
--- a/main.c Fri Sep 1 13:31:59 2006
+++ b/main.c Wed Sep 6 12:06:50 2006
@@ -20,6 +20,7 @@
char stext[1024];
Bool *seltag;
int bx, by, bw, bh, bmw, mw, screen, sx, sy, sw, sh;
+int doevent = 1, doingevent;
unsigned int ntags, numlockmask;
Atom wmatom[WMLast], netatom[NetLast];
Bool running = True;
@@ -268,11 +269,19 @@
drawstatus();
scan();
+ /* Let's get masochist here. */
+ startupnp();
+
/* main event loop, also reads status text from stdin */
XSync(dpy, False);
procevent();
readin = True;
while(running) {
+ if(!doevent) {
+ sleep(1);
+ continue;
+ }
+
FD_ZERO(&rd);
if(readin)
FD_SET(STDIN_FILENO, &rd);
@@ -280,6 +289,7 @@
r = select(xfd + 1, &rd, NULL, NULL, NULL);
if((r == -1) && (errno == EINTR))
continue;
+ doingevent = 1;
if(r > 0) {
if(readin && FD_ISSET(STDIN_FILENO, &rd)) {
readin = NULL != fgets(stext, sizeof(stext), stdin);
@@ -292,10 +302,13 @@
}
else if(r < 0)
eprint("select failed\n");
- procevent();
+ if(doevent)
+ procevent();
+ doingevent = 0;
}
cleanup();
XCloseDisplay(dpy);
+ endupnp();
return 0;
}
diff -r 74739798b0b2 dwm-service.xml
--- /dev/null Fri Sep 1 13:31:59 2006
+++ b/dwm-service.xml Wed Sep 6 12:06:50 2006
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<scpd xmlns="urn:schemas-upnp-org:service-1-0">
+ <specVersion>
+ <major>1</major>
+ <minor>0</minor>
+ </specVersion>
+ <actionList>
+ <action>
+ <name>tag</name>
+ <argumentList>
+ <argument>
+ <name>tagnum</name>
+ <direction>in</direction>
+ <relatedStateVariable>tagtype</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ <action>
+ <name>zoom</name>
+ </action>
+ <action>
+ <name>setstatus</name>
+ <argumentList>
+ <argument>
+ <name>status</name>
+ <direction>in</direction>
+ <relatedStateVariable>statustype</relatedStateVariable>
+ </argument>
+ </argumentList>
+ </action>
+ </actionList>
+ <serviceStateTable>
+ <stateVariable sendEvents="no">
+ <name>tagtype</name>
+ <dataType>ui2</dataType>
+ <defaultValue>0</defaultValue>
+ </stateVariable>
+ <stateVariable>
+ <name>statustype</name>
+ <dataType>string</dataType>
+ <defaultValue>Undefined</defaultValue>
+ </stateVariable>
+ </serviceStateTable>
+</scpd>
+
diff -r 74739798b0b2 dwm.xml
--- /dev/null Fri Sep 1 13:31:59 2006
+++ b/dwm.xml Wed Sep 6 12:06:50 2006
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+ <specVersion>
+ <major>1</major>
+ <minor>0</minor>
+ </specVersion>
+ <device>
+ <deviceType>urn:schemas-upnp-org:device:Application:1</deviceType>
+ <friendlyName>Dynamic Window Manager</friendlyName>
+ <manufacturer>10kloc.org</manufacturer>
+ <manufacturerURL>http://www.10kloc.org</manufacturerURL>
+ <modelName>DWM</modelName>
+ <UDN>uuid:00000000-abcd-0000-abcd-000000000000</UDN>
+ <iconList>
+ <icon>
+ <mimetype>image/gif</mimetype>
+ <width>118</width>
+ <height>119</height>
+ <depth>32</depth>
+ <url>/dwm.gif</url>
+ </icon>
+ </iconList>
+ <serviceList>
+ <service>
+ <serviceType>urn:schemas-dwm:service:dwm:1</serviceType>
+ <serviceId>urn:dwm:serviceId:dwm1</serviceId>
+ <controlURL>/dwm</controlURL>
+ <eventSubURL>/dwm</eventSubURL>
+ <SCPDURL>/dwm-service.xml</SCPDURL>
+ </service>
+ </serviceList>
+ </device>
+</root>
+
diff -r 74739798b0b2 upnp.c
--- /dev/null Fri Sep 1 13:31:59 2006
+++ b/upnp.c Wed Sep 6 12:06:50 2006
@@ -0,0 +1,260 @@
+#include "dwm.h"
+#include <stdlib.h>
+#include <upnp/ixml.h>
+#include <string.h>
+#include <upnp/upnp.h>
+#include <upnp/upnptools.h>
+
+char *dwmUDN;
+ithread_mutex_t umutex = PTHREAD_MUTEX_INITIALIZER;
+int devicehandle;
+
+char *
+getfirstdocitem(IXML_Document *doc, const char *item)
+{
+ IXML_NodeList *nodelist;
+ IXML_Node *textnode;
+ IXML_Node *tmpnode;
+ char *ret;
+
+ textnode = tmpnode = NULL;
+ ret = NULL;
+ nodelist = NULL;
+
+ nodelist = ixmlDocument_getElementsByTagName(doc, (char *)item);
+ if(nodelist) {
+ tmpnode = ixmlNodeList_item(nodelist, 0);
+ if(tmpnode) {
+ textnode = ixmlNode_getFirstChild(tmpnode);
+ if(textnode != NULL)
+ ret = strdup(ixmlNode_getNodeValue(textnode));
+ }
+ }
+ if(nodelist)
+ ixmlNodeList_free(nodelist);
+
+ return ret;
+}
+
+int
+dwmsubscribereq(struct Upnp_Subscription_Request *e)
+{
+ IXML_Document *propSet;
+
+ propSet = NULL;
+
+ ithread_mutex_lock(&umutex);
+
+ if(strcmp(e->UDN, dwmUDN) == 0) {
+ UpnpAddToPropertySet(&propSet, "running", "1");
+ UpnpAcceptSubscriptionExt(devicehandle, e->UDN, e->ServiceId,
+ propSet, e->Sid);
+ ixmlDocument_free(propSet);
+ }
+
+ ithread_mutex_unlock(&umutex);
+
+ return 1;
+}
+
+struct Upnp_Action_Request *
+mkanswer(struct Upnp_Action_Request *e, int succ)
+{
+ char resultstr[513];
+
+ if(succ) {
+ snprintf(resultstr, sizeof(resultstr), "<u:%sResponse "
+ "xmlns:u=\""
+ "urn:schemas-dwm:service:dwm\">\n\n"
+ "</u:%sResponse>", e->ActionName, e->ActionName);
+ e->ErrCode = UPNP_E_SUCCESS;
+ e->ActionResult = ixmlParseBuffer(resultstr);
+ } else {
+ e->ErrCode = 402;
+ strcpy(e->ErrStr, "Invalid Args");
+ e->ActionResult = NULL;
+ }
+
+ return e;
+}
+
+void
+waitfordwm(void)
+{
+
+ doevent = 0;
+ while(doingevent)
+ sleep(1);
+}
+
+void
+allowdwm(void)
+{
+
+ doevent = 1;
+}
+
+int
+dodwmstatus(struct Upnp_Action_Request *e)
+{
+ char *status;
+ int succ;
+
+ succ = 0;
+
+ status = getfirstdocitem(e->ActionRequest, "status");
+ if(status != NULL) {
+ strncpy(stext, status, sizeof(stext) - 1);
+ stext[sizeof(stext) - 1] = '\0';
+ waitfordwm();
+ drawstatus();
+ allowdwm();
+ succ = 1;
+ }
+
+ e = mkanswer(e, succ);
+
+ return e->ErrCode;
+}
+
+int
+dodwmtag(struct Upnp_Action_Request *e)
+{
+ char *tagnum;
+ int succ;
+ Arg arg;
+
+ succ = 0;
+
+ tagnum = getfirstdocitem(e->ActionRequest, "tagnum");
+ if(tagnum != NULL) {
+ arg.i = atoi(tagnum);
+ waitfordwm();
+ tag(&arg);
+ allowdwm();
+ succ = 1;
+ }
+
+ e = mkanswer(e, succ);
+
+ return e->ErrCode;
+}
+
+int
+dodwmzoom(struct Upnp_Action_Request *e)
+{
+
+ waitfordwm();
+ zoom(NULL);
+ allowdwm();
+
+ e = mkanswer(e, 1);
+
+ return e->ErrCode;
+}
+
+int
+dwmactionreq(struct Upnp_Action_Request *e)
+{
+ int result;
+
+ result = 0;
+
+ ithread_mutex_lock(&umutex);
+
+ if(strcmp(e->DevUDN, dwmUDN) == 0) {
+ if(strcmp(e->ActionName, "tag") == 0)
+ result = dodwmtag(e);
+ else if(strcmp(e->ActionName, "zoom") == 0)
+ result = dodwmzoom(e);
+ else if(strcmp(e->ActionName, "status") == 0)
+ result = dodwmstatus(e);
+ }
+
+ ithread_mutex_unlock(&umutex);
+
+ return result;
+}
+
+int
+dwmupnpcallback(Upnp_EventType et, void *e, void *c)
+{
+
+ switch(et) {
+ case UPNP_EVENT_SUBSCRIPTION_REQUEST:
+ dwmsubscribereq((struct Upnp_Subscription_Request *)e);
+ break;
+ case UPNP_CONTROL_GET_VAR_REQUEST:
+ break;
+ case UPNP_CONTROL_ACTION_REQUEST:
+ dwmactionreq((struct Upnp_Action_Request *)e);
+ break;
+ default:
+ eprint("dwm: unknown event type for UPnP: %d\n", et);
+ }
+
+ return 0;
+}
+
+int
+statetableinit(char *descurl)
+{
+ IXML_Document *ixmldoc;
+ int ret;
+
+ ret = UpnpDownloadXmlDoc(descurl, &ixmldoc);
+ if(ret != UPNP_E_SUCCESS) {
+ UpnpFinish();
+ eprint("dwm: Could not parse description document.\n");
+ }
+
+ dwmUDN = getfirstdocitem(ixmldoc, "UDN");
+ ixmlDocument_free(ixmldoc);
+
+ return ret;
+}
+
+void
+startupnp(void)
+{
+ char descurl[513];
+ int ret;
+
+ ret = UpnpInit(NULL, 0);
+ if(ret != UPNP_E_SUCCESS) {
+ UpnpFinish();
+ eprint("dwm: Error in UpnpInit -- %d\n", ret);
+ }
+
+ ret = UpnpSetWebServerRootDir("/var/dwm/upnp");
+ if(ret != UPNP_E_SUCCESS) {
+ UpnpFinish();
+ eprint("dwm: Error specifying root directory -- %d\n", ret);
+ }
+
+ snprintf(descurl, 512, "http://%s:%d/%s", UpnpGetServerIpAddress(),
+ UpnpGetServerPort(), "dwm.xml");
+ ret = UpnpRegisterRootDevice(descurl, dwmupnpcallback, &devicehandle,
+ &devicehandle);
+ if(ret != UPNP_E_SUCCESS) {
+ UpnpFinish();
+ eprint("dwm: Error registering the rootdevice: %d\n", ret);
+ }
+
+ statetableinit(descurl);
+
+ ret = UpnpSendAdvertisement(devicehandle, 100);
+ if(ret != UPNP_E_SUCCESS) {
+ UpnpFinish();
+ eprint("dwm: Error sending advertisements: %d\n", ret);
+ }
+}
+
+void
+endupnp(void)
+{
+
+ UpnpUnRegisterRootDevice(devicehandle);
+ UpnpFinish();
+}
+