diff --git a/libXmd/include/Xmd/Dir.h b/libXmd/include/Xmd/Dir.h new file mode 100644 index 0000000..ae7d71b --- /dev/null +++ b/libXmd/include/Xmd/Dir.h @@ -0,0 +1,10 @@ +#ifndef _XmdDir_h +#define _XmdDir_h + +#include + +/* XmdDirGetUser returns the location of the Xmd user directory, which is + located at $HOME/.Xmd */ +String XmdDirGetUser (); + +#endif diff --git a/libXmd/include/Xmd/Error.h b/libXmd/include/Xmd/Error.h new file mode 100644 index 0000000..df2659b --- /dev/null +++ b/libXmd/include/Xmd/Error.h @@ -0,0 +1,12 @@ +#ifndef _XmdError_h +#define _XmdError_h + +typedef enum { + XmdErrorNone, + XmdErrorOutOfMemory, + XmdErrorFileNotFound, + XmdErrorCantOpenFile, + XmdErrorBadSyntax +} XmdError; + +#endif diff --git a/libXmd/include/Xmd/Replicant.h b/libXmd/include/Xmd/Replicant.h index 2d94e92..6158c86 100644 --- a/libXmd/include/Xmd/Replicant.h +++ b/libXmd/include/Xmd/Replicant.h @@ -11,8 +11,11 @@ /* XmdReplicantState contains state information for a replicant instance. */ typedef struct _XmdReplicantState XmdReplicantState; -typedef int (*XmdReplicantVersion) (); -typedef Widget (*XmdReplicantCreate) (Widget parent, XmdReplicantState *state); +typedef int (*XmdReplicantFnVersion) (); +typedef void (*XmdReplicantFnConstruct) (); +typedef void (*XmdReplicantFnDestruct) (); +typedef Widget (*XmdReplicantFnCreate) ( + Widget parent, XmdReplicantState *state); /* XmdReplicantOpen opens a replicant from the file located at path. Replicant files hold state and point to shared object files containing their code. @@ -36,6 +39,12 @@ typedef Widget (*XmdReplicantCreate) (Widget parent, XmdReplicantState *state); Version must return the value of XmdREPLICANT_VERSION. This is used to detect the API version that the replicant was compiled with, and applications with a different API version will refuse to load it. + + void NAME_XmdReplicantConstruct (); + Construct is called when the shared object is loaded. + + void NAME_XmdReplicantDestruct (); + Descruct is called before the shared object is unloaded. Widget NAME_XmdReplicantCreate (Widget parent, XmdReplicantState *state); Create instantiates a new widget representing the replicant given by state. diff --git a/libXmd/src/Dir.c b/libXmd/src/Dir.c new file mode 100644 index 0000000..cffa2ed --- /dev/null +++ b/libXmd/src/Dir.c @@ -0,0 +1,8 @@ +#include +#include + +String XmdDirGetUser () { + String dir = NULL; + XtAsprintf(&dir, "%s/.Xmd", getenv("HOME")); + return dir; +} diff --git a/libXmd/src/Replicant.c b/libXmd/src/Replicant.c index 678d1b3..32a8785 100644 --- a/libXmd/src/Replicant.c +++ b/libXmd/src/Replicant.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -15,8 +16,10 @@ typedef struct { int references; struct { - XmdReplicantVersion version; - XmdReplicantCreate create; + XmdReplicantFnVersion version; + XmdReplicantFnConstruct construct; + XmdReplicantFnDestruct destruct; + XmdReplicantFnCreate create; } symbols; } XmdReplicantSource; @@ -31,8 +34,8 @@ static void XmdReplicantHandleDestroy ( Widget replicant, XtPointer clientData, XtPointer callData); -static void XmdReplicantStateSave (XmdReplicantState *state); -static void XmdReplicantStateLoad (XmdReplicantState *state); +static XmdError XmdReplicantStateSave (XmdReplicantState *state); +static XmdError XmdReplicantStateLoad (XmdReplicantState *state); static XmdReplicantState *XmdReplicantStateNew (); static void XmdReplicantStateFree (XmdReplicantState *state); static XmdReplicantSource *XmdReplicantSourceOpen (ConstString name); @@ -41,6 +44,7 @@ static XmdReplicantSource *XmdReplicantSourceGet (ConstString name); static String XmdReplicantScanDir ( ConstString path, ConstString name); +static void XmdReplicantEnsure (); static const String defaultReplicantPath = "/usr/lib/Xmd/replicants:" @@ -80,6 +84,7 @@ static void XmdReplicantHandleDestroy ( ) { (void)(replicant); (void)(callData); + XmdReplicantEnsure(); XmdReplicantState *state = clientData; if (state == NULL) return; @@ -104,12 +109,15 @@ String XmdReplicantResolveName (ConstString rawName) { char ch = *list; if (dirBuffer == NULL) dirBuffer = XmdBufferNew(char); - XmdBufferPush(dirBuffer, &ch); if (ch == ':' || ch == 0) { + char null = 0; + XmdBufferPush(dirBuffer, &null); String dir = XmdBufferBreak(dirBuffer); dirBuffer = NULL; file = XmdReplicantScanDir(dir, name); XtFree(dir); + } else { + XmdBufferPush(dirBuffer, &ch); } list ++; } @@ -172,12 +180,13 @@ static Bool writeMapValue ( return True; } -static void XmdReplicantStateSave (XmdReplicantState *state) { +static XmdError XmdReplicantStateSave (XmdReplicantState *state) { FILE *file = fopen(state->file, "w"); - if (file == NULL) return; + if (file == NULL) return XmdErrorCantOpenFile; fprintf(file, "[%s]\n", state->sourceName); XmdStringMapIterate(state->map, writeMapValue, file); fclose(file); + return XmdErrorNone; } static String readEscapedMapString (FILE *file, char delimiter) { @@ -208,23 +217,42 @@ static String readEscapedMapString (FILE *file, char delimiter) { return XmdBufferBreak(string); } -static void XmdReplicantStateLoad (XmdReplicantState *state) { - FILE *file = fopen(state->file, "w"); - if (file == NULL) return; +static XmdError XmdReplicantStateLoad (XmdReplicantState *state) { + if (state->sourceName != NULL) XtFree(state->sourceName); + state->sourceName = NULL; - int ch = 0; - while (isspace((ch == fgetc(file)))); - if (ch != '[') return; + FILE *file = fopen(state->file, "r"); + if (file == NULL) { + return XmdErrorFileNotFound; + }; + + int ch = ' '; + while (isspace(ch)) ch = fgetc(file); + if (ch != '[') { + fclose(file); + return XmdErrorBadSyntax; + } state->sourceName = readEscapedMapString(file, ']'); ch = fgetc(file); - if (ch != '\n') return; + if (ch != '\n') { + fclose(file); + return XmdErrorBadSyntax; + } while (1) { String key = readEscapedMapString(file, '='); String value = readEscapedMapString(file, '\n'); XmdStringMapSet(state->map, key, value); XtFree(key); - if (value == NULL) break; + if (value == NULL) { + if (key == NULL) { + fclose(file); + return XmdErrorNone; + } else { + fclose(file); + return XmdErrorBadSyntax; + } + } } } @@ -276,18 +304,30 @@ static XmdReplicantSource *XmdReplicantSourceOpen (ConstString name) { XtFree(path); if (source->handle == NULL) goto fail; - /* find symbols */ - String versionName = NULL; - XtAsprintf(&versionName, "%s_XmdReplicantVersion", name); - source->symbols.version = (XmdReplicantVersion) ( - dlsym(source->handle, versionName)); - XtFree(versionName); + /* find symbols */ + String symbolName = NULL; + XtAsprintf(&symbolName, "%s_XmdReplicantVersion", name); + source->symbols.version = (XmdReplicantFnVersion) ( + dlsym(source->handle, symbolName)); + XtFree(symbolName); if (source->symbols.version == NULL) goto fail; - String createName = NULL; - XtAsprintf(&createName, "%s_XmdReplicantCreate", name); - source->symbols.create = (XmdReplicantCreate) ( - dlsym(source->handle, createName)); - XtFree(createName); + + XtAsprintf(&symbolName, "%s_XmdReplicantConstruct", name); + source->symbols.construct = (XmdReplicantFnConstruct) ( + dlsym(source->handle, symbolName)); + XtFree(symbolName); + if (source->symbols.construct == NULL) goto fail; + + XtAsprintf(&symbolName, "%s_XmdReplicantDestruct", name); + source->symbols.destruct = (XmdReplicantFnDestruct) ( + dlsym(source->handle, symbolName)); + XtFree(symbolName); + if (source->symbols.destruct == NULL) goto fail; + + XtAsprintf(&symbolName, "%s_XmdReplicantCreate", name); + source->symbols.create = (XmdReplicantFnCreate) ( + dlsym(source->handle, symbolName)); + XtFree(symbolName); if (source->symbols.create == NULL) goto fail; /* check if the version matches */ @@ -307,6 +347,8 @@ static void XmdReplicantSourceClose (XmdReplicantSource *source) { } static XmdReplicantSource *XmdReplicantSourceGet (ConstString name) { + XmdReplicantEnsure(); + /* check to see if it has already been loaded */ XmdReplicantSource *source = XmdStringMapGet(resident, name); if (source != NULL) return source; @@ -316,7 +358,9 @@ static XmdReplicantSource *XmdReplicantSourceGet (ConstString name) { } static String XmdReplicantScanDir (ConstString path, ConstString name) { - DIR *dir = opendir(path); + DIR *dir = opendir(path); + if (dir == NULL) return NULL; + String result = NULL; while (1) { struct dirent *entry = readdir(dir); @@ -330,3 +374,9 @@ static String XmdReplicantScanDir (ConstString path, ConstString name) { closedir(dir); return result; } + +static void XmdReplicantEnsure () { + if (resident == NULL) { + resident = XmdStringMapNew(); + } +} diff --git a/replicants/Launcher/build.sh b/replicants/Launcher/build.sh new file mode 100755 index 0000000..053ed94 --- /dev/null +++ b/replicants/Launcher/build.sh @@ -0,0 +1,2 @@ +#!/bin/sh +../../scripts/buildreplicant.sh Launcher "$@" diff --git a/xmpanel/src/icons/appCalculator.xbm b/replicants/Launcher/src/icons/appCalculator.xbm similarity index 100% rename from xmpanel/src/icons/appCalculator.xbm rename to replicants/Launcher/src/icons/appCalculator.xbm diff --git a/xmpanel/src/icons/appEditor.xbm b/replicants/Launcher/src/icons/appEditor.xbm similarity index 100% rename from xmpanel/src/icons/appEditor.xbm rename to replicants/Launcher/src/icons/appEditor.xbm diff --git a/xmpanel/src/icons/appFiles.xbm b/replicants/Launcher/src/icons/appFiles.xbm similarity index 100% rename from xmpanel/src/icons/appFiles.xbm rename to replicants/Launcher/src/icons/appFiles.xbm diff --git a/xmpanel/src/icons/appMail.xbm b/replicants/Launcher/src/icons/appMail.xbm similarity index 100% rename from xmpanel/src/icons/appMail.xbm rename to replicants/Launcher/src/icons/appMail.xbm diff --git a/xmpanel/src/icons/appMusic.xbm b/replicants/Launcher/src/icons/appMusic.xbm similarity index 100% rename from xmpanel/src/icons/appMusic.xbm rename to replicants/Launcher/src/icons/appMusic.xbm diff --git a/xmpanel/src/icons/appTerminal.xbm b/replicants/Launcher/src/icons/appTerminal.xbm similarity index 100% rename from xmpanel/src/icons/appTerminal.xbm rename to replicants/Launcher/src/icons/appTerminal.xbm diff --git a/xmpanel/src/icons/appWebBrowser.xbm b/replicants/Launcher/src/icons/appWebBrowser.xbm similarity index 100% rename from xmpanel/src/icons/appWebBrowser.xbm rename to replicants/Launcher/src/icons/appWebBrowser.xbm diff --git a/replicants/Launcher/src/icons/unknown.xbm b/replicants/Launcher/src/icons/unknown.xbm new file mode 100644 index 0000000..beeafcd --- /dev/null +++ b/replicants/Launcher/src/icons/unknown.xbm @@ -0,0 +1,27 @@ +#define unknown_width 48 +#define unknown_height 48 +static unsigned char unknown_bits[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0xff, 0x01, 0x00, 0x00, 0x00, 0xf8, 0xff, 0x01, 0x00, + 0x00, 0x00, 0xf8, 0xff, 0x03, 0x00, 0x00, 0x00, 0xf8, 0xf8, 0x03, 0x00, + 0x00, 0x00, 0x30, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x10, 0xe0, 0x03, 0x00, + 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, + 0x00, 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, + 0x00, 0x00, 0x00, 0xf0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, + 0x00, 0x00, 0x00, 0xf8, 0x01, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x07, 0x00, 0x00, + 0x00, 0x00, 0xe0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, + 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, + 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x80, 0x1f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; diff --git a/replicants/Launcher/src/main.c b/replicants/Launcher/src/main.c new file mode 100644 index 0000000..95082a2 --- /dev/null +++ b/replicants/Launcher/src/main.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include + +#include "icons/appCalculator.xbm" +#include "icons/appEditor.xbm" +#include "icons/appFiles.xbm" +#include "icons/appMail.xbm" +#include "icons/appMusic.xbm" +#include "icons/appTerminal.xbm" +#include "icons/appWebBrowser.xbm" +#include "icons/unknown.xbm" + +void activateLauncher (Widget button, XtPointer clientData, XtPointer callData) { + (void)(button); + (void)(callData); + XmdReplicantState *state = (XmdReplicantState *)(clientData); + + String command = XmdReplicantStateQuery(state, "Exec"); + String fullCommand = NULL; + XtAsprintf(&fullCommand, "%s &", command); + XtFree(command); + system(fullCommand); + XtFree(fullCommand); +} + +int Launcher_XmdReplicantVersion () { + return XmdREPLICANT_VERSION; +} + +void Launcher_XmdReplicantConstruct () { + +} + +void Launcher_XmdReplicantDestruct () { + +} + +void handleDestroyFreePixmap ( + Widget replicant, + XtPointer clientData, + XtPointer callData +) { + (void)(callData); + XFreePixmap(XtDisplay(replicant), (Pixmap)(clientData)); +} + +Widget Launcher_XmdReplicantCreate (Widget parent, XmdReplicantState *state) { + String iconName = XmdReplicantStateQuery(state, "Icon"); + Pixmap icon; + #define iconCase(name) if (strcmp(#name, iconName) == 0) {\ + icon = XmdLoadBitmapIcon(parent, app##name);\ + } else + iconCase(Calculator) + iconCase(Editor) + iconCase(Files) + iconCase(Mail) + iconCase(Terminal) + iconCase(WebBrowser) + iconCase(Music) + icon = XmdLoadBitmapIcon(parent, unknown); + + XtFree(iconName); + + Widget button = XtVaCreateManagedWidget ( + "launcher", xmPushButtonWidgetClass, parent, + XmNleftAttachment, XmATTACH_FORM, + XmNlabelType, XmPIXMAP, + XmNlabelPixmap, icon, + NULL); + XtAddCallback ( + button, XmNactivateCallback, + activateLauncher, (XtPointer)(state)); + XtAddCallback ( + button, XmNdestroyCallback, + handleDestroyFreePixmap, (XtPointer)(icon)); + return button; +} diff --git a/scripts/buildreplicant.sh b/scripts/buildreplicant.sh new file mode 100755 index 0000000..2c43cf8 --- /dev/null +++ b/scripts/buildreplicant.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +. `dirname $0`/flags.sh + +function build() { + if + mkdir -p lib && \ + cc src/*.c -o "lib/$1.so" -shared $CFLAGS + then + echo ".// ok" + return 0 + else + echo "XXX FAIL!" + return 1 + fi +} + +function clean() { + rm -f bin/* +} + +case "$2" in +install) + clean; build "$1" + mkdir -p "$PREFIX/lib/Xmd/replicants" + cp "lib/$1.so" "$PREFIX/lib/Xmd/replicants" + clean + ;; +clean) + clean + ;; +*) + build "$1" +esac diff --git a/scripts/flags.sh b/scripts/flags.sh index 38d9d40..f5ab2fe 100644 --- a/scripts/flags.sh +++ b/scripts/flags.sh @@ -2,4 +2,4 @@ CFLAGS="-std=c99 -Wall -Wextra -Werror -fPIC" PREFIX="/usr/local" -APP_LIBS="-lXmd -lXm -lXt -lX11" +APP_LIBS="-lXmd -lXm -lXt -lX11 -ldl" diff --git a/xmpanel/src/main.c b/xmpanel/src/main.c index 493baed..7ff22a7 100644 --- a/xmpanel/src/main.c +++ b/xmpanel/src/main.c @@ -2,32 +2,20 @@ #include #include #include +#include #include +#include #include #include #include - -#include "icons/appCalculator.xbm" -#include "icons/appEditor.xbm" -#include "icons/appFiles.xbm" -#include "icons/appMail.xbm" -#include "icons/appTerminal.xbm" -#include "icons/appWebBrowser.xbm" -#include "icons/appMusic.xbm" +#include #include "icons/icon.xbm" -typedef struct { - Pixmap icon; - const char * command; -} Launcher; - -void createAllLaunchers (Widget); -Widget createLauncher (Widget, Launcher); -void activateLauncher (Widget, XtPointer, XtPointer); - XtAppContext application; +static void loadReplicants (Widget parent); + int main (int argc, char *argv[]) { Widget window = XtVaAppInitialize ( &application, "Panel", @@ -47,41 +35,42 @@ int main (int argc, char *argv[]) { "layout", xmRowColumnWidgetClass, window, XmNorientation, XmHORIZONTAL, NULL); - createAllLaunchers(layout); + loadReplicants(layout); XtManageChild(layout); XtRealizeWidget(window); XtAppMainLoop(application); } -void createAllLaunchers (Widget parent) { - #define add(name, cmd) createLauncher(parent, (Launcher){\ - .icon = XmdLoadBitmapIcon(parent, app##name),\ - .command = cmd " &"\ - } ); - add(Calculator, "xcalc"); - add(Editor, "nedit"); - add(Files, "caja"); - add(Mail, "nedit"); - add(Terminal, "uxterm"); - add(WebBrowser, "firefox"); - add(Music, "ymuse"); - #undef add -} - -Widget createLauncher (Widget parent, Launcher launcher) { - Widget button = XtVaCreateManagedWidget ( - "launcher", xmPushButtonWidgetClass, parent, - XmNleftAttachment, XmATTACH_FORM, - XmNlabelType, XmPIXMAP, - XmNlabelPixmap, launcher.icon, - NULL); - XtAddCallback(button, XmNactivateCallback, activateLauncher, (XtPointer)(launcher.command)); - return button; -} - -void activateLauncher (Widget button, XtPointer clientData, XtPointer callData) { - (void)(button); - (void)(callData); - system((char *)(clientData)); +static void loadReplicants (Widget parent) { + String userDir = XmdDirGetUser(); + String replicantsDir = NULL; + XtAsprintf(&replicantsDir, "%s/panel", userDir); + XtFree(userDir); + + struct dirent **entries = NULL; + int entriesCount = scandir(replicantsDir, &entries, NULL, alphasort); + if (entriesCount < 0) { + XtFree(replicantsDir); + /* TODO error message */ + return; + } + + for (int index = 0; index < entriesCount; index ++) { + struct dirent *entry = entries[index]; + String point = NULL; + if ((point = strrchr(entry->d_name,'.')) != NULL) { + if (strcmp(point, ".replicant") == 0) { + String fullName = NULL; + XtAsprintf ( + &fullName, "%s/%s", + replicantsDir, entry->d_name); + XmdReplicantOpen(parent, fullName); + XtFree(fullName); + } + } + XtFree((char *)(entry)); + } + XtFree((char *)(entries)); + XtFree(replicantsDir); }