First replicant yay

This commit is contained in:
Sasha Koshka 2023-11-13 19:05:51 -05:00
parent 55dfca6341
commit a6fa746ef3
18 changed files with 299 additions and 78 deletions

10
libXmd/include/Xmd/Dir.h Normal file
View File

@ -0,0 +1,10 @@
#ifndef _XmdDir_h
#define _XmdDir_h
#include <X11/Intrinsic.h>
/* XmdDirGetUser returns the location of the Xmd user directory, which is
located at $HOME/.Xmd */
String XmdDirGetUser ();
#endif

View File

@ -0,0 +1,12 @@
#ifndef _XmdError_h
#define _XmdError_h
typedef enum {
XmdErrorNone,
XmdErrorOutOfMemory,
XmdErrorFileNotFound,
XmdErrorCantOpenFile,
XmdErrorBadSyntax
} XmdError;
#endif

View File

@ -11,8 +11,11 @@
/* XmdReplicantState contains state information for a replicant instance. */ /* XmdReplicantState contains state information for a replicant instance. */
typedef struct _XmdReplicantState XmdReplicantState; typedef struct _XmdReplicantState XmdReplicantState;
typedef int (*XmdReplicantVersion) (); typedef int (*XmdReplicantFnVersion) ();
typedef Widget (*XmdReplicantCreate) (Widget parent, XmdReplicantState *state); typedef void (*XmdReplicantFnConstruct) ();
typedef void (*XmdReplicantFnDestruct) ();
typedef Widget (*XmdReplicantFnCreate) (
Widget parent, XmdReplicantState *state);
/* XmdReplicantOpen opens a replicant from the file located at path. Replicant /* XmdReplicantOpen opens a replicant from the file located at path. Replicant
files hold state and point to shared object files containing their code. 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 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 the API version that the replicant was compiled with, and applications with a
different API version will refuse to load it. 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); Widget NAME_XmdReplicantCreate (Widget parent, XmdReplicantState *state);
Create instantiates a new widget representing the replicant given by state. Create instantiates a new widget representing the replicant given by state.

8
libXmd/src/Dir.c Normal file
View File

@ -0,0 +1,8 @@
#include <Xmd/Dir.h>
#include <stdlib.h>
String XmdDirGetUser () {
String dir = NULL;
XtAsprintf(&dir, "%s/.Xmd", getenv("HOME"));
return dir;
}

View File

@ -2,6 +2,7 @@
#include <Xmd/StringMap.h> #include <Xmd/StringMap.h>
#include <Xmd/Map.h> #include <Xmd/Map.h>
#include <Xmd/Buffer.h> #include <Xmd/Buffer.h>
#include <Xmd/Error.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@ -15,8 +16,10 @@ typedef struct {
int references; int references;
struct { struct {
XmdReplicantVersion version; XmdReplicantFnVersion version;
XmdReplicantCreate create; XmdReplicantFnConstruct construct;
XmdReplicantFnDestruct destruct;
XmdReplicantFnCreate create;
} symbols; } symbols;
} XmdReplicantSource; } XmdReplicantSource;
@ -31,8 +34,8 @@ static void XmdReplicantHandleDestroy (
Widget replicant, Widget replicant,
XtPointer clientData, XtPointer clientData,
XtPointer callData); XtPointer callData);
static void XmdReplicantStateSave (XmdReplicantState *state); static XmdError XmdReplicantStateSave (XmdReplicantState *state);
static void XmdReplicantStateLoad (XmdReplicantState *state); static XmdError XmdReplicantStateLoad (XmdReplicantState *state);
static XmdReplicantState *XmdReplicantStateNew (); static XmdReplicantState *XmdReplicantStateNew ();
static void XmdReplicantStateFree (XmdReplicantState *state); static void XmdReplicantStateFree (XmdReplicantState *state);
static XmdReplicantSource *XmdReplicantSourceOpen (ConstString name); static XmdReplicantSource *XmdReplicantSourceOpen (ConstString name);
@ -41,6 +44,7 @@ static XmdReplicantSource *XmdReplicantSourceGet (ConstString name);
static String XmdReplicantScanDir ( static String XmdReplicantScanDir (
ConstString path, ConstString path,
ConstString name); ConstString name);
static void XmdReplicantEnsure ();
static const String defaultReplicantPath = static const String defaultReplicantPath =
"/usr/lib/Xmd/replicants:" "/usr/lib/Xmd/replicants:"
@ -80,6 +84,7 @@ static void XmdReplicantHandleDestroy (
) { ) {
(void)(replicant); (void)(replicant);
(void)(callData); (void)(callData);
XmdReplicantEnsure();
XmdReplicantState *state = clientData; XmdReplicantState *state = clientData;
if (state == NULL) return; if (state == NULL) return;
@ -104,12 +109,15 @@ String XmdReplicantResolveName (ConstString rawName) {
char ch = *list; char ch = *list;
if (dirBuffer == NULL) dirBuffer = XmdBufferNew(char); if (dirBuffer == NULL) dirBuffer = XmdBufferNew(char);
XmdBufferPush(dirBuffer, &ch);
if (ch == ':' || ch == 0) { if (ch == ':' || ch == 0) {
char null = 0;
XmdBufferPush(dirBuffer, &null);
String dir = XmdBufferBreak(dirBuffer); String dir = XmdBufferBreak(dirBuffer);
dirBuffer = NULL; dirBuffer = NULL;
file = XmdReplicantScanDir(dir, name); file = XmdReplicantScanDir(dir, name);
XtFree(dir); XtFree(dir);
} else {
XmdBufferPush(dirBuffer, &ch);
} }
list ++; list ++;
} }
@ -172,12 +180,13 @@ static Bool writeMapValue (
return True; return True;
} }
static void XmdReplicantStateSave (XmdReplicantState *state) { static XmdError XmdReplicantStateSave (XmdReplicantState *state) {
FILE *file = fopen(state->file, "w"); FILE *file = fopen(state->file, "w");
if (file == NULL) return; if (file == NULL) return XmdErrorCantOpenFile;
fprintf(file, "[%s]\n", state->sourceName); fprintf(file, "[%s]\n", state->sourceName);
XmdStringMapIterate(state->map, writeMapValue, file); XmdStringMapIterate(state->map, writeMapValue, file);
fclose(file); fclose(file);
return XmdErrorNone;
} }
static String readEscapedMapString (FILE *file, char delimiter) { static String readEscapedMapString (FILE *file, char delimiter) {
@ -208,23 +217,42 @@ static String readEscapedMapString (FILE *file, char delimiter) {
return XmdBufferBreak(string); return XmdBufferBreak(string);
} }
static void XmdReplicantStateLoad (XmdReplicantState *state) { static XmdError XmdReplicantStateLoad (XmdReplicantState *state) {
FILE *file = fopen(state->file, "w"); if (state->sourceName != NULL) XtFree(state->sourceName);
if (file == NULL) return; state->sourceName = NULL;
int ch = 0; FILE *file = fopen(state->file, "r");
while (isspace((ch == fgetc(file)))); if (file == NULL) {
if (ch != '[') return; return XmdErrorFileNotFound;
};
int ch = ' ';
while (isspace(ch)) ch = fgetc(file);
if (ch != '[') {
fclose(file);
return XmdErrorBadSyntax;
}
state->sourceName = readEscapedMapString(file, ']'); state->sourceName = readEscapedMapString(file, ']');
ch = fgetc(file); ch = fgetc(file);
if (ch != '\n') return; if (ch != '\n') {
fclose(file);
return XmdErrorBadSyntax;
}
while (1) { while (1) {
String key = readEscapedMapString(file, '='); String key = readEscapedMapString(file, '=');
String value = readEscapedMapString(file, '\n'); String value = readEscapedMapString(file, '\n');
XmdStringMapSet(state->map, key, value); XmdStringMapSet(state->map, key, value);
XtFree(key); 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); XtFree(path);
if (source->handle == NULL) goto fail; if (source->handle == NULL) goto fail;
/* find symbols */ /* find symbols */
String versionName = NULL; String symbolName = NULL;
XtAsprintf(&versionName, "%s_XmdReplicantVersion", name); XtAsprintf(&symbolName, "%s_XmdReplicantVersion", name);
source->symbols.version = (XmdReplicantVersion) ( source->symbols.version = (XmdReplicantFnVersion) (
dlsym(source->handle, versionName)); dlsym(source->handle, symbolName));
XtFree(versionName); XtFree(symbolName);
if (source->symbols.version == NULL) goto fail; if (source->symbols.version == NULL) goto fail;
String createName = NULL;
XtAsprintf(&createName, "%s_XmdReplicantCreate", name); XtAsprintf(&symbolName, "%s_XmdReplicantConstruct", name);
source->symbols.create = (XmdReplicantCreate) ( source->symbols.construct = (XmdReplicantFnConstruct) (
dlsym(source->handle, createName)); dlsym(source->handle, symbolName));
XtFree(createName); 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; if (source->symbols.create == NULL) goto fail;
/* check if the version matches */ /* check if the version matches */
@ -307,6 +347,8 @@ static void XmdReplicantSourceClose (XmdReplicantSource *source) {
} }
static XmdReplicantSource *XmdReplicantSourceGet (ConstString name) { static XmdReplicantSource *XmdReplicantSourceGet (ConstString name) {
XmdReplicantEnsure();
/* check to see if it has already been loaded */ /* check to see if it has already been loaded */
XmdReplicantSource *source = XmdStringMapGet(resident, name); XmdReplicantSource *source = XmdStringMapGet(resident, name);
if (source != NULL) return source; if (source != NULL) return source;
@ -316,7 +358,9 @@ static XmdReplicantSource *XmdReplicantSourceGet (ConstString name) {
} }
static String XmdReplicantScanDir (ConstString path, 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; String result = NULL;
while (1) { while (1) {
struct dirent *entry = readdir(dir); struct dirent *entry = readdir(dir);
@ -330,3 +374,9 @@ static String XmdReplicantScanDir (ConstString path, ConstString name) {
closedir(dir); closedir(dir);
return result; return result;
} }
static void XmdReplicantEnsure () {
if (resident == NULL) {
resident = XmdStringMapNew();
}
}

2
replicants/Launcher/build.sh Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
../../scripts/buildreplicant.sh Launcher "$@"

View File

@ -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 };

View File

@ -0,0 +1,80 @@
#include <Xm/Xm.h>
#include <Xm/PushB.h>
#include <Xmd/Icon.h>
#include <Xmd/Replicant.h>
#include <stdlib.h>
#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;
}

34
scripts/buildreplicant.sh Executable file
View File

@ -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

View File

@ -2,4 +2,4 @@
CFLAGS="-std=c99 -Wall -Wextra -Werror -fPIC" CFLAGS="-std=c99 -Wall -Wextra -Werror -fPIC"
PREFIX="/usr/local" PREFIX="/usr/local"
APP_LIBS="-lXmd -lXm -lXt -lX11" APP_LIBS="-lXmd -lXm -lXt -lX11 -ldl"

View File

@ -2,32 +2,20 @@
#include <Xm/Xm.h> #include <Xm/Xm.h>
#include <Xm/RowColumn.h> #include <Xm/RowColumn.h>
#include <Xm/PushB.h> #include <Xm/PushB.h>
#include <Xmd/Dir.h>
#include <Xmd/Icon.h> #include <Xmd/Icon.h>
#include <Xmd/Replicant.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <dirent.h>
#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 "icons/icon.xbm" #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; XtAppContext application;
static void loadReplicants (Widget parent);
int main (int argc, char *argv[]) { int main (int argc, char *argv[]) {
Widget window = XtVaAppInitialize ( Widget window = XtVaAppInitialize (
&application, "Panel", &application, "Panel",
@ -47,41 +35,42 @@ int main (int argc, char *argv[]) {
"layout", xmRowColumnWidgetClass, window, "layout", xmRowColumnWidgetClass, window,
XmNorientation, XmHORIZONTAL, XmNorientation, XmHORIZONTAL,
NULL); NULL);
createAllLaunchers(layout); loadReplicants(layout);
XtManageChild(layout); XtManageChild(layout);
XtRealizeWidget(window); XtRealizeWidget(window);
XtAppMainLoop(application); XtAppMainLoop(application);
} }
void createAllLaunchers (Widget parent) { static void loadReplicants (Widget parent) {
#define add(name, cmd) createLauncher(parent, (Launcher){\ String userDir = XmdDirGetUser();
.icon = XmdLoadBitmapIcon(parent, app##name),\ String replicantsDir = NULL;
.command = cmd " &"\ XtAsprintf(&replicantsDir, "%s/panel", userDir);
} ); XtFree(userDir);
add(Calculator, "xcalc");
add(Editor, "nedit"); struct dirent **entries = NULL;
add(Files, "caja"); int entriesCount = scandir(replicantsDir, &entries, NULL, alphasort);
add(Mail, "nedit"); if (entriesCount < 0) {
add(Terminal, "uxterm"); XtFree(replicantsDir);
add(WebBrowser, "firefox"); /* TODO error message */
add(Music, "ymuse"); return;
#undef add }
}
for (int index = 0; index < entriesCount; index ++) {
Widget createLauncher (Widget parent, Launcher launcher) { struct dirent *entry = entries[index];
Widget button = XtVaCreateManagedWidget ( String point = NULL;
"launcher", xmPushButtonWidgetClass, parent, if ((point = strrchr(entry->d_name,'.')) != NULL) {
XmNleftAttachment, XmATTACH_FORM, if (strcmp(point, ".replicant") == 0) {
XmNlabelType, XmPIXMAP, String fullName = NULL;
XmNlabelPixmap, launcher.icon, XtAsprintf (
NULL); &fullName, "%s/%s",
XtAddCallback(button, XmNactivateCallback, activateLauncher, (XtPointer)(launcher.command)); replicantsDir, entry->d_name);
return button; XmdReplicantOpen(parent, fullName);
} XtFree(fullName);
}
void activateLauncher (Widget button, XtPointer clientData, XtPointer callData) { }
(void)(button); XtFree((char *)(entry));
(void)(callData); }
system((char *)(clientData)); XtFree((char *)(entries));
XtFree(replicantsDir);
} }