summaryrefslogtreecommitdiffstats
path: root/unix/tkUnixSysNotify.c
diff options
context:
space:
mode:
Diffstat (limited to 'unix/tkUnixSysNotify.c')
-rwxr-xr-xunix/tkUnixSysNotify.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/unix/tkUnixSysNotify.c b/unix/tkUnixSysNotify.c
new file mode 100755
index 0000000..b9e635a
--- /dev/null
+++ b/unix/tkUnixSysNotify.c
@@ -0,0 +1,233 @@
+/*
+ * tkUnixSysNotify.c --
+ *
+ * tkUnixSysNotify.c implements a "sysnotify" Tcl command which
+ * permits one to post system notifications based on the libnotify API.
+ *
+ * Copyright © 2020 Kevin Walzer/WordTech Communications LLC.
+ * Copyright © 2020 Christian Werner for runtime linking
+ *
+ * See the file "license.terms" for information on usage and redistribution of
+ * this file, and for a DISCLAIMER OF ALL WARRANTIES.
+ */
+
+#include "tkInt.h"
+#include "tkUnixInt.h"
+
+/*
+ * Runtime linking of libnotify.
+ */
+
+typedef int (*fn_ln_init)(const char *);
+typedef void (*fn_ln_uninit)(void);
+typedef void * (*fn_ln_notification_new)(const char *, const char *,
+ const char *, void *);
+typedef int (*fn_ln_notification_show)(void *, int *);
+
+static struct {
+ int nopen;
+ Tcl_LoadHandle lib;
+ fn_ln_init init;
+ fn_ln_uninit uninit;
+ fn_ln_notification_new notification_new;
+ fn_ln_notification_show notification_show;
+} ln_fns = {
+ 0, NULL, NULL, NULL, NULL, NULL
+};
+
+#define notify_init ln_fns.init
+#define notify_uninit ln_fns.uninit
+#define notify_notification_new ln_fns.notification_new
+#define notify_notification_show ln_fns.notification_show
+
+TCL_DECLARE_MUTEX(ln_mutex);
+
+/*
+ * Forward declarations for procedures defined in this file.
+ */
+
+static Tcl_CmdDeleteProc SysNotifyDeleteCmd;
+static Tcl_ObjCmdProc SysNotifyCmd;
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SysNotifyDeleteCmd --
+ *
+ * Delete notification and clean up.
+ *
+ * Results:
+ * Window destroyed.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static void
+SysNotifyDeleteCmd (
+ TCL_UNUSED(void *))
+{
+ Tcl_MutexLock(&ln_mutex);
+ if (--ln_fns.nopen == 0) {
+ if (notify_uninit) {
+ notify_uninit();
+ }
+ if (ln_fns.lib != NULL) {
+ Tcl_FSUnloadFile(NULL, ln_fns.lib);
+ }
+ memset(&ln_fns, 0, sizeof(ln_fns));
+ }
+ Tcl_MutexUnlock(&ln_mutex);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SysNotifyCreateCmd --
+ *
+ * Create tray command and (unreal) window.
+ *
+ * Results:
+ * Icon tray and hidden window created.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+static int
+SysNotifyCmd(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ const char *title;
+ const char *message;
+ const char *icon;
+ const char *appname = "Wish";
+ void *notif;
+
+ if (objc < 3) {
+ Tcl_WrongNumArgs(interp, 1, objv, "title message");
+ return TCL_ERROR;
+ }
+
+ /*
+ * Pass strings to notification, and use a sane platform-specific
+ * icon in the alert.
+ */
+
+ title = Tcl_GetString(objv[1]);
+ message = Tcl_GetString(objv[2]);
+ icon = "dialog-information";
+
+
+ /* Use the appname for libnotify initialization
+ * See bug f63c37be3a for a discussion whether this should be
+ * allowed at all on safe interpreters
+ */
+ if (!Tcl_IsSafe(interp)) {
+ appname = ((TkWindow *)Tk_MainWindow(interp))->nameUid;
+ }
+
+ /*
+ * Call to notify_init should go here to prevent test suite failure.
+ */
+
+ if (notify_init && notify_notification_new && notify_notification_show) {
+ Tcl_Encoding enc;
+ Tcl_DString dst, dsm;
+
+ Tcl_DStringInit(&dst);
+ Tcl_DStringInit(&dsm);
+ enc = Tcl_GetEncoding(NULL, "utf-8");
+ char *dstStr = Tcl_UtfToExternalDString(enc, title, TCL_INDEX_NONE, &dst);
+ char *dsmStr = Tcl_UtfToExternalDString(enc, message, TCL_INDEX_NONE, &dsm);
+ notify_init(appname);
+ notif = notify_notification_new(dstStr, dsmStr, icon, NULL);
+ notify_notification_show(notif, NULL);
+ Tcl_DStringFree(&dsm);
+ Tcl_DStringFree(&dst);
+ Tcl_FreeEncoding(enc);
+ }
+
+ return TCL_OK;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * SysNotify_Init --
+ *
+ * Initialize the command.
+ *
+ * Results:
+ * Command initialized.
+ *
+ * Side effects:
+ * None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+int
+SysNotify_Init(
+ Tcl_Interp *interp)
+{
+ Tcl_MutexLock(&ln_mutex);
+ if (ln_fns.nopen == 0) {
+ int i = 0;
+ Tcl_Obj *nameobj;
+ static const char *lnlibs[] = {
+ "libnotify.so.4",
+ "libnotify.so.3",
+ "libnotify.so.2",
+ "libnotify.so.1",
+ "libnotify.so",
+ NULL
+ };
+
+ while (lnlibs[i] != NULL) {
+ Tcl_ResetResult(interp);
+ nameobj = Tcl_NewStringObj(lnlibs[i], TCL_INDEX_NONE);
+ Tcl_IncrRefCount(nameobj);
+ if (Tcl_LoadFile(interp, nameobj, NULL, 0, NULL, &ln_fns.lib)
+ == TCL_OK) {
+ Tcl_DecrRefCount(nameobj);
+ break;
+ }
+ Tcl_DecrRefCount(nameobj);
+ ++i;
+ }
+ if (ln_fns.lib != NULL) {
+#define LN_SYM(name) \
+ ln_fns.name = (fn_ln_ ## name) \
+ Tcl_FindSymbol(NULL, ln_fns.lib, "notify_" #name)
+ LN_SYM(init);
+ LN_SYM(uninit);
+ LN_SYM(notification_new);
+ LN_SYM(notification_show);
+#undef LN_SYM
+ }
+ }
+ ln_fns.nopen++;
+ Tcl_MutexUnlock(&ln_mutex);
+ if (!notify_init) return TCL_OK;
+
+ Tcl_CreateObjCommand(interp, "::tk::sysnotify::_sysnotify", SysNotifyCmd,
+ interp, SysNotifyDeleteCmd);
+ return TCL_OK;
+}
+
+/*
+ * Local Variables:
+ * mode: c
+ * c-basic-offset: 4
+ * fill-column: 78
+ * coding: utf-8
+ * End:
+ */