XmShelf stub

This commit is contained in:
Sasha Koshka 2024-02-01 00:49:58 -05:00
parent b3b86808ea
commit 58ef75e455
3 changed files with 365 additions and 0 deletions

2
xmshelf/build.sh Executable file
View File

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

View File

@ -0,0 +1,27 @@
#define icon_width 48
#define icon_height 48
static unsigned char icon_bits[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f,
0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0c, 0x1e, 0x00, 0xf0, 0x03, 0x30,
0xfc, 0xf3, 0xff, 0x0f, 0xfc, 0x3f, 0x0c, 0x09, 0x00, 0xf2, 0x13, 0x30,
0xfc, 0xf9, 0xff, 0x0d, 0xec, 0x3f, 0x0c, 0x09, 0x80, 0x02, 0x50, 0x30,
0xfc, 0xf9, 0xff, 0x02, 0xd0, 0x3f, 0x8c, 0x04, 0x40, 0x01, 0xa6, 0x30,
0xfc, 0xfc, 0x7f, 0x81, 0xa1, 0x3f, 0x8c, 0xfc, 0x4f, 0xe1, 0xa0, 0x30,
0xfc, 0x04, 0x78, 0x41, 0xa0, 0x3f, 0x4c, 0x06, 0x48, 0x41, 0xa0, 0x30,
0x7c, 0x06, 0x78, 0x81, 0xa0, 0x3f, 0x4c, 0x06, 0x88, 0x82, 0x50, 0x30,
0x7c, 0x06, 0xf8, 0x03, 0xf0, 0x3f, 0x2c, 0x05, 0x28, 0x0c, 0x0c, 0x31,
0x3c, 0x07, 0x38, 0xf0, 0x03, 0x3f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f,
0xac, 0x00, 0x00, 0x00, 0x00, 0x35, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f,
0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0c, 0x00, 0x00, 0x22, 0x00, 0x30,
0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f, 0x0c, 0x00, 0x00, 0x7f, 0x00, 0x30,
0xfc, 0xff, 0xff, 0xc1, 0xff, 0x3f, 0xcc, 0x3f, 0x00, 0x41, 0x3e, 0x31,
0x7c, 0xc0, 0xff, 0xe3, 0x23, 0x3f, 0x4c, 0xc0, 0x1f, 0x14, 0x22, 0x31,
0x7c, 0x00, 0xf0, 0xf7, 0x23, 0x31, 0x4c, 0x00, 0xf0, 0x94, 0x22, 0x31,
0x7c, 0x00, 0xd0, 0xf7, 0x22, 0x31, 0x4c, 0x00, 0xf0, 0x94, 0x22, 0x31,
0x7c, 0x00, 0xd0, 0xf7, 0xe2, 0x31, 0x4c, 0x00, 0xf0, 0x94, 0x22, 0x3f,
0x7c, 0x00, 0xd0, 0xf7, 0x36, 0x31, 0x4c, 0xff, 0xf7, 0x94, 0x22, 0x31,
0x7c, 0x00, 0xd0, 0xf7, 0x22, 0x31, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f,
0xac, 0x00, 0x00, 0x00, 0x00, 0x35, 0xfc, 0xff, 0xff, 0xff, 0xff, 0x3f,
0xcc, 0xff, 0xff, 0xff, 0xff, 0x33, 0xfc, 0x00, 0x00, 0x00, 0x00, 0x3f,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

336
xmshelf/src/main.c Normal file
View File

@ -0,0 +1,336 @@
#define _XOPEN_SOURCE
#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/PushB.h>
#include <Xm/MainW.h>
#include <Xm/IconG.h>
#include <Xm/Container.h>
#include <Xm/ScrolledW.h>
#include <Xmd/Icon.h>
#include <Xmd/Exec.h>
#include <Xmd/Buffer.h>
#include <Xmd/StringMap.h>
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
#include <dirent.h>
#include <libgen.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include "icons/icon.xbm"
XtAppContext application;
typedef enum {
DeviceKindGeneric,
DeviceKindDisk,
DeviceKindRom,
DeviceKindFilesystem,
DeviceKindTTY
} DeviceKind;
typedef struct {
Widget widget;
DeviceKind kind;
Bool orphaned;
String name;
String mountPoint;
String label;
} Device;
void loadIconPixmaps (void);
void refreshDevices (void);
void refreshDisks (void);
Device * DeviceNew (String name, DeviceKind kind);
void DeviceDelete (ConstString name);
void DeviceFree (Device *this);
void DeviceSetKind (Device *this, DeviceKind kind);
void DeviceSetMountPoint (Device *this, ConstString mountPoint);
void DeviceSetLabel (Device *this, ConstString label);
int readBlockDevice (FILE *stream, Device **);
int readLsblkPair (FILE *stream, String *key, String *value);
void handleRefresh (Widget, XtPointer, XtPointer);
int main (int argc, char *argv[]) {
devices = XmdStringMapNew();
Widget window = XtVaAppInitialize (
&application, "Shelf",
NULL, 0,
&argc, argv,
NULL,
XmNtitle, "Shelf",
XmNiconName, "Shelf",
XmNwidth, 256,
XmNheight, 256,
NULL);
Widget layout = XtVaCreateWidget (
"layout", xmFormWidgetClass, window,
XmNorientation, XmVERTICAL,
NULL);
Pixmap iconPixmap = XmdLoadBitmapIcon(layout, icon);
XtVaSetValues (
window,
XmNiconPixmap, iconPixmap,
NULL);
Pixmap refreshPixmap = XmdLoadBitmapIcon(layout, refresh);
XmString string = XmStringCreateLocalized("Refresh");
Widget refreshButton = XtVaCreateManagedWidget (
"refreshButton", xmPushButtonWidgetClass, layout,
XmNleftAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_FORM,
XmNlabelType, XmPIXMAP_AND_STRING,
XmNlabelPixmap, refreshPixmap,
XmNlabelString, string,
NULL);
XmStringFree(string);
XtAddCallback (
refreshButton, XmNactivateCallback,
handleRefresh, NULL);
Pixmap mountPixmap = XmdLoadBitmapIcon(layout, mount);
string = XmStringCreateLocalized("Mount");
Widget mountButton = XtVaCreateManagedWidget (
"mountButton", xmPushButtonWidgetClass, layout,
XmNleftAttachment, XmATTACH_WIDGET,
XmNleftWidget, refreshButton,
XmNbottomAttachment, XmATTACH_FORM,
XmNlabelType, XmPIXMAP_AND_STRING,
XmNlabelPixmap, mountPixmap,
XmNlabelString, string,
NULL);
XmStringFree(string);
Pixmap unmountPixmap = XmdLoadBitmapIcon(layout, unmount);
string = XmStringCreateLocalized("Unmount");
/*Widget unmountButton = */XtVaCreateManagedWidget (
"unmountButton", xmPushButtonWidgetClass, layout,
XmNleftAttachment, XmATTACH_WIDGET,
XmNleftWidget, mountButton,
XmNbottomAttachment, XmATTACH_FORM,
XmNlabelType, XmPIXMAP_AND_STRING,
XmNlabelPixmap, unmountPixmap,
XmNlabelString, string,
NULL);
XmStringFree(string);
Widget scroller = XtVaCreateWidget (
"scroll", xmScrolledWindowWidgetClass, layout,
XmNscrollingPolicy, XmAUTOMATIC,
XmNleftAttachment, XmATTACH_FORM,
XmNtopAttachment, XmATTACH_FORM,
XmNrightAttachment, XmATTACH_FORM,
XmNbottomAttachment, XmATTACH_WIDGET,
XmNbottomWidget, refreshButton,
NULL);
container = XtVaCreateManagedWidget (
"container", xmContainerWidgetClass, scroller,
XmNlayoutType, XmSPATIAL,
XmNspatialStyle, XmGRID,
NULL);
loadIconPixmaps();
refreshDevices();
XtManageChild(scroller);
XtManageChild(layout);
XtRealizeWidget(window);
XtAppMainLoop(application);
}
void loadIconPixmaps (void) {
#define dev(kind, name) \
iconsLarge[DeviceKind##kind] = XmdLoadBitmapIcon(container, name); \
iconsSmall[DeviceKind##kind] = XmdLoadBitmapIcon(container, name##_small)
dev(Generic, generic);
dev(Disk, disk);
dev(Rom, rom);
dev(Filesystem, filesystem);
dev(TTY, tty);
}
void handleRefresh (Widget button, XtPointer clientData, XtPointer callData) {
(void)(button);
(void)(clientData);
(void)(callData);
refreshDevices();
}
Bool markDevice (XmdStringMap *map, ConstString key, void *value, void *data) {
(void)(map);
(void)(key);
(void)(data);
Device *this = value;
this->orphaned = True;
return True;
}
Bool transferDevice (XmdStringMap *map, ConstString key, void *value, void *data) {
(void)(map);
(void)(key);
(void)(data);
Device *this = value;
if (this->orphaned) {
DeviceFree(this);
} else {
XmdStringMapSet(devices, key, value);
}
return True;
}
void refreshDevices (void) {
XtUnmanageChild(container);
/* pre-mark all devices as orphaned */
XmdStringMapIterate(devices, markDevice, NULL);
/* refresh devices by category */
refreshDisks();
/* create a new map, only moving over extant devices */
XmdStringMap *oldMap = devices;
devices = XmdStringMapNew();
XmdStringMapIterate(oldMap, transferDevice, NULL);
XmdStringMapFree(oldMap);
XtManageChild(container);
}
String diskName (ConstString directory, ConstString name) {
char fullpath[PATH_MAX];
snprintf(fullpath, XtNumber(fullpath), "%s/%s", directory, name);
char resolved[PATH_MAX];
return XtNewString(basename(realpath(fullpath, resolved)));
}
void refreshDisks (void) {
/* get disks */
ConstString disksById = "/dev/disk/by-id";
ConstString disksByLabel = "/dev/disk/by-label";
ConstString disksByPartuuid = "/dev/disk/by-partuuid";
struct dirent **entries = NULL;
int entriesCount = scandir(disksById, &entries, NULL, alphasort);
if (entriesCount < 0) {
/* TODO error message */
return;
}
for (int index = 0; index < entriesCount; index ++) {
struct dirent *entry = entries[index];
if (entry->d_name[0] == '.') {
XtFree((char *)(entry));
continue;
}
String deviceName = diskName(disksById, entry->d_name);
XtFree((char *)(entry));
Device *device = XmdStringMapGet(devices, deviceName);
if (device == NULL) {
device = DeviceNew(deviceName, DeviceKindDisk);
} else {
device->orphaned = False;
DeviceSetKind(device, DeviceKindDisk);
}
XtFree(deviceName);
}
XtFree((char *)(entries));
/* determine disk types */
DIR *dir = opendir(disksByPartuuid);
if (dir == NULL) {
/* TODO error message */
return;
}
struct dirent *entry;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') continue;
String deviceName = diskName(disksByPartuuid, entry->d_name);
Device *device = XmdStringMapGet(devices, deviceName);
if (device != NULL) DeviceSetKind(device, DeviceKindFilesystem);
XtFree(deviceName);
}
closedir(dir);
/* determine partition labels */
dir = opendir(disksByLabel);
if (dir == NULL) {
/* TODO error message */
return;
}
while ((entry = readdir(dir)) != NULL) {
if (entry->d_name[0] == '.') continue;
String deviceName = diskName(disksByLabel, entry->d_name);
Device *device = XmdStringMapGet(devices, deviceName);
if (device != NULL) DeviceSetLabel(device, entry->d_name);
XtFree(deviceName);
}
closedir(dir);
}
Device *DeviceNew (String name, DeviceKind kind) {
Device *this = XtNew(Device);
this->mountPoint = NULL;
this->label = NULL;
this->name = XtNewString(name);
this->orphaned = False;
XmString string = XmStringCreateLocalized(name);
this->widget = XtVaCreateManagedWidget (
"device", xmIconGadgetClass, container,
XmNalignment, XmALIGNMENT_BEGINNING,
XmNlabelString, string,
NULL);
XmStringFree(string);
DeviceSetKind(this, kind);
XmdStringMapSet(devices, name, this);
return this;
}
void DeviceDelete (ConstString name) {
DeviceFree(XmdStringMapSet(devices, name, NULL));
}
void DeviceFree (Device *this) {
if (this == NULL) return;
XtDestroyWidget(this->widget);
XtFree((char *)(this->name));
XtFree((char *)(this->mountPoint));
XtFree((char *)(this->label));
XtFree((char *)(this));
}
void DeviceSetKind (Device *this, DeviceKind kind) {
XtVaSetValues (
this->widget,
XmNlargeIconPixmap, iconsLarge[kind],
XmNsmallIconPixmap, iconsSmall[kind],
NULL);
this->kind = kind;
}
void DeviceSetMountPoint (Device *this, ConstString mountPoint) {
XtFree(this->mountPoint);
this->mountPoint = XtNewString(mountPoint);
}
void DeviceSetLabel (Device *this, ConstString label) {
XtFree(this->label);
this->label = XtNewString(label);
XmString string = NULL;
if (label == NULL) {
string = XmStringCreateLocalized(this->name);
} else {
string = XmStringCreateLocalized(this->label);
}
XtVaSetValues (
this->widget,
XmNlabelString, string,
NULL);
XmStringFree(string);
}