diff options
-rw-r--r-- | urgent.c | 103 | ||||
-rw-r--r-- | urgent.tcl | 144 |
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 |