aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--urgent.c103
-rw-r--r--urgent.tcl144
2 files changed, 247 insertions, 0 deletions
diff --git a/urgent.c b/urgent.c
new file mode 100644
index 0000000..42f01ad
--- /dev/null
+++ b/urgent.c
@@ -0,0 +1,103 @@
+/*
+ * $Id$
+ *
+ * Compile with
+ * gcc -L/usr/X11R6/lib -lX11 urgent.c -o urgent
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+typedef long (*FLAGMANIP) (long);
+
+static long set_urgency(long flags)
+{
+ return flags | XUrgencyHint;
+}
+
+static long clear_urgency(long flags)
+{
+ return flags & ~XUrgencyHint;
+}
+
+static long toggle_urgency(long flags)
+{
+ if ((flags & XUrgencyHint) == 0) {
+ return set_urgency(flags);
+ } else {
+ return clear_urgency(flags);
+ }
+}
+
+static int apply(Display *dpy, Window id, FLAGMANIP f)
+{
+ XWMHints *hints = XGetWMHints(dpy, id);
+
+ if (hints == NULL)
+ return 0;
+
+ hints->flags = f(hints->flags);
+
+ return (XSetWMHints(dpy, id, hints) != 0);
+}
+
+int main(int argc, char *argv[])
+{
+ const char helpstr[] = "Usage: urgent {-set|-clear|-toggle} XWINID\n";
+ Display *dpy;
+ Window id;
+ FLAGMANIP action;
+
+ if (argc == 2 && strcmp(argv[1], "-help") == 0) {
+ printf("Sets, clears or toggles the \"urgency\" flag "
+ "for a specified X window using its WM_HINTS property\n");
+ printf(helpstr);
+ return EXIT_SUCCESS;
+ }
+
+ if (argc != 3) {
+ fprintf(stderr, "Wrong number of agruments\n");
+ fprintf(stderr, helpstr);
+ return EXIT_FAILURE;
+ }
+
+ if (strcmp(argv[1], "-set") == 0) {
+ action = set_urgency;
+ } else if (strcmp(argv[1], "-clear") == 0) {
+ action = clear_urgency;
+ } else if (strcmp(argv[1], "-toggle") == 0) {
+ action = toggle_urgency;
+ } else {
+ fprintf(stderr, "Invalid action \"%s\", "
+ "must be one of -set, -clear or -toggle\n", argv[1]);
+ return EXIT_FAILURE;
+ }
+
+ errno = 0;
+ id = strtol(argv[2], NULL, 16);
+ if (errno != 0) {
+ fprintf(stderr, "Invalid X window id \"%s\", "
+ "must be a hexadecimal integer\n", argv[2]);
+ return EXIT_FAILURE;
+ }
+
+ dpy = XOpenDisplay(NULL);
+
+ if (dpy == NULL) {
+ fprintf(stderr, "Unable to open display.");
+ return EXIT_FAILURE;
+ }
+
+ apply(dpy, id, action);
+
+ XFlush(dpy);
+
+ XCloseDisplay(dpy);
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/urgent.tcl b/urgent.tcl
new file mode 100644
index 0000000..af59ecc
--- /dev/null
+++ b/urgent.tcl
@@ -0,0 +1,144 @@
+# $Id$
+
+namespace eval urgent {
+ custom::defgroup Urgent [::msgcat::mc "Urgency hinting."] -group Plugins
+
+ custom::defvar options(enabled) 1 \
+ [::msgcat::mc "Set the urgency hint on Tkabber's chat\
+ window when a new message is received."] \
+ -type boolean \
+ -group Urgent
+
+ custom::defvar options(handle_personal_messages) 1 \
+ [::msgcat::mc "React on messages addressed to us."] \
+ -type boolean \
+ -group Urgent
+
+ custom::defvar options(handle_normal_messages) 0 \
+ [::msgcat::mc "React on messages in MUC rooms\
+ which are not addressed to us."] \
+ -type boolean \
+ -group Urgent
+
+ custom::defvar options(handle_server_messages) 0 \
+ [::msgcat::mc "React on messages generated by the server"] \
+ -type boolean \
+ -group Urgent
+}
+
+proc urgent::chat_message_notify {chatid from type body extras} {
+ variable options
+
+ if {!$options(enabled)} return
+
+ set delayed [::xmpp::delay::exists $extras]
+ if {$delayed} return
+
+ switch -- $type {
+ groupchat {
+ if {[string equal [chat::get_jid $chatid] $from]} {
+ if {$options(handle_server_messages)} {
+ notify $chatid
+ }
+ } else {
+ set mynick [chat::get_nick [chat::get_xlib $chatid] \
+ [chat::our_jid $chatid] $type]
+ if {[check_message $mynick $body]} {
+ if {$options(handle_personal_messages)} {
+ notify $chatid
+ }
+ } else {
+ if {$options(handle_normal_messages)} {
+ notify $chatid
+ }
+ }
+ }
+ }
+ chat {
+ foreach xelem $extras {
+ ::xmpp::xml::split $xelem tag xmlns attrs cdata subels
+ # Don't play sound if this 'empty' tag is present. It indicates
+ # messages history in chat window.
+ if {[string equal $tag ""] && \
+ [string equal $xmlns tkabber:x:nolog]} {
+ return
+ }
+ }
+
+ if {$from == "" && $options(handle_server_messages)} {
+ notify $chatid
+ } elseif {$options(handle_personal_messages)} {
+ notify $chatid
+ }
+ }
+ }
+}
+
+proc urgent::notify {chatid} {
+ variable options
+ variable xwinids
+
+ exec $options(program) -set $xwinids($chatid)
+}
+
+proc urgent::xwinid {win} {
+ # Parent window id: 0x2e0001e "Tkabber"
+ set data [exec xwininfo -children -id [winfo id $win]]
+ if {[regexp {Parent window id: (\S+)} $data -> id]} {
+ return $id
+ } else {
+ return ""
+ }
+}
+
+proc urgent::root_winid {xwinid _chatid} {
+ return $xwinid
+}
+
+proc urgent::chat_winid {chatid} {
+ xwinid [winfo id [chat::winid $chatid]]
+}
+
+proc urgent::record_xwinid {chatid _type} {
+ variable xwinids
+ set xwinids($chatid) [winid $chatid]
+}
+
+proc urgent::clear_urgency_hint {winid} {
+ variable options
+ variable xwinids
+
+ set chatid [chat::winid_to_chatid $winid]
+
+ exec $options(program) -clear $xwinids($chatid)
+}
+
+namespace eval urgent {
+ variable options
+
+ if {![info exists options(program)]} {
+ set options(program) [file join \
+ [file dirname [info script]] urgent]
+ }
+ if {![file executable $options(program)]} {
+ puts stderr [::msgcat::mc "Urgency hint setting program \"%s\"\
+ is not available or not executed by the current user.\
+ The \"urgent\" plugin is disabled. Consult its README file."
+ $options(program)]
+ set options(enabled) 0
+ }
+
+ if {$::ifacetk::options(use_tabbar)} {
+ interp alias {} [namespace current]::winid \
+ {} [namespace current]::root_winid [xwinid .]
+ } else {
+ interp alias {} [namespace current]::winid \
+ {} [namespace current]::chat_winid
+ }
+
+ hook::add open_chat_post_hook [namespace current]::record_xwinid 40
+ hook::add draw_message_hook [namespace current]::chat_message_notify 19
+ hook::add got_focus_hook [namespace current]::clear_urgency_hint
+}
+
+# vim:ts=8:sw=4:sts=4:noet