#define _XOPEN_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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); }