mdebase/mpanel/startbutton/module.c

491 lines
11 KiB
C
Raw Normal View History

2025-12-18 11:43:16 +09:00
#include "config.h"
2025-12-21 23:34:08 +09:00
#define _MILSKO
2025-12-23 18:21:44 +09:00
#include <MDE/Foundation.h>
2025-12-18 11:43:16 +09:00
#include <Mw/Milsko.h>
2025-12-24 23:42:12 +09:00
#include <xemil.h>
2025-12-28 18:17:24 +09:00
2025-12-23 18:21:44 +09:00
#include <ini.h>
2025-12-28 18:17:24 +09:00
#include <stb_image.h>
#include <stb_ds.h>
2025-12-23 18:21:44 +09:00
#include <stdlib.h>
#include <string.h>
2025-12-23 22:29:18 +09:00
#include <unistd.h>
2025-12-18 11:43:16 +09:00
#define TriW 5
2025-12-21 23:34:08 +09:00
typedef struct opaque {
MwLLPixmap closed;
MwLLPixmap opened;
MwWidget submenu;
int is_opened;
MwMenu menu;
2025-12-25 00:19:55 +09:00
MwMenu current;
2025-12-23 18:21:44 +09:00
2025-12-25 05:01:19 +09:00
MwLLPixmap stripe;
char* stripe_color;
2025-12-23 18:21:44 +09:00
char* name;
char* category;
2025-12-23 22:29:18 +09:00
char* exec;
2025-12-21 23:34:08 +09:00
} opaque_t;
2025-12-23 18:21:44 +09:00
static int dumper(void* user, const char* section, const char* name, const char* value) {
opaque_t* opaque = user;
if(strcmp(section, "Desktop Entry") != 0) return 1;
2025-12-23 22:29:18 +09:00
if(strcmp(name, "Name") == 0) {
2025-12-28 17:29:00 +09:00
opaque->name = MDEStringDuplicate(value);
2025-12-23 22:29:18 +09:00
} else if(strcmp(name, "Categories") == 0) {
2025-12-28 17:29:00 +09:00
opaque->category = MDEStringDuplicate(value);
2025-12-23 22:29:18 +09:00
} else if(strcmp(name, "Exec") == 0) {
2025-12-28 17:29:00 +09:00
opaque->exec = MDEStringDuplicate(value);
2025-12-23 18:21:44 +09:00
}
return 1;
}
enum CATEGORY {
C_UNKNOWN = 0,
C_AUDIOVIDEO,
C_AUDIO,
C_VIDEO,
C_DEVELOPMENT,
C_EDUCATION,
C_GAME,
C_GRAPHICS,
C_NETWORK,
C_OFFICE,
C_SCIENCE,
C_SETTINGS,
C_SYSTEM,
C_UTILITY
};
static const char* category_mapping[] = {
2025-12-23 22:29:18 +09:00
"Lost & Found",
"Multimedia",
"Audio",
"Video",
"Development",
"Education",
"Game",
"Graphics",
"Network",
"Office",
"Science",
"Settings",
"System",
"Utility"};
static void call(const char* name, int dir, int symlink, void* user) {
2025-12-23 18:21:44 +09:00
opaque_t* opaque = user;
2025-12-23 22:29:18 +09:00
int c = C_UNKNOWN;
2025-12-23 18:21:44 +09:00
2025-12-23 22:29:18 +09:00
opaque->name = NULL;
2025-12-23 18:21:44 +09:00
opaque->category = NULL;
2025-12-23 22:29:18 +09:00
opaque->exec = NULL;
2025-12-23 18:21:44 +09:00
ini_parse(name, dumper, user);
2025-12-23 22:29:18 +09:00
if(opaque->category != NULL) {
2025-12-23 18:21:44 +09:00
char* str = opaque->category;
2025-12-23 22:29:18 +09:00
while(str != NULL) {
2025-12-23 18:21:44 +09:00
char* s = strchr(str, ';');
2025-12-23 22:29:18 +09:00
if(s != NULL) {
2025-12-23 18:21:44 +09:00
s[0] = 0;
}
2025-12-23 22:29:18 +09:00
if(strcasecmp(str, "AudioVideo") == 0) {
2025-12-23 18:21:44 +09:00
c = C_AUDIOVIDEO;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Audio") == 0) {
2025-12-23 18:21:44 +09:00
c = C_AUDIO;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Video") == 0) {
2025-12-23 18:21:44 +09:00
c = C_VIDEO;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Development") == 0) {
2025-12-23 18:21:44 +09:00
c = C_DEVELOPMENT;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Education") == 0) {
2025-12-23 18:21:44 +09:00
c = C_EDUCATION;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Game") == 0) {
2025-12-23 18:21:44 +09:00
c = C_GAME;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Graphics") == 0) {
2025-12-23 18:21:44 +09:00
c = C_GRAPHICS;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Network") == 0) {
2025-12-23 18:21:44 +09:00
c = C_NETWORK;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Office") == 0) {
2025-12-23 18:21:44 +09:00
c = C_OFFICE;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Science") == 0) {
2025-12-23 18:21:44 +09:00
c = C_SCIENCE;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Settings") == 0) {
2025-12-23 18:21:44 +09:00
c = C_SETTINGS;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "System") == 0) {
2025-12-23 18:21:44 +09:00
c = C_SYSTEM;
2025-12-23 22:29:18 +09:00
} else if(strcasecmp(str, "Utility") == 0) {
2025-12-23 18:21:44 +09:00
c = C_UTILITY;
}
str = s;
if(str != NULL) str++;
}
free(opaque->category);
}
2025-12-23 22:29:18 +09:00
if(opaque->name != NULL) {
int i;
2025-12-23 18:21:44 +09:00
MwMenu mc = NULL, m;
2025-12-25 00:19:55 +09:00
for(i = 0; i < arrlen(opaque->current->sub); i++) {
if(strcmp(opaque->current->sub[i]->name, category_mapping[c]) == 0) {
mc = opaque->current->sub[i];
2025-12-23 18:21:44 +09:00
break;
}
}
2025-12-23 22:29:18 +09:00
if(mc == NULL) {
2025-12-28 17:29:00 +09:00
mc = malloc(sizeof(*mc));
mc->name = MDEStringDuplicate(category_mapping[c]);
mc->keep = 0;
mc->sub = NULL;
mc->wsub = NULL;
mc->opaque = NULL;
2025-12-23 18:21:44 +09:00
2025-12-25 00:19:55 +09:00
arrput(opaque->current->sub, mc);
2025-12-23 18:21:44 +09:00
}
2025-12-23 22:29:18 +09:00
for(i = 0; i < arrlen(mc->sub); i++) {
if(strcmp(mc->sub[i]->name, opaque->name) == 0) {
2025-12-23 18:21:44 +09:00
break;
}
}
2025-12-23 22:29:18 +09:00
if(i == arrlen(mc->sub)) {
m = malloc(sizeof(*m));
m->name = opaque->name;
m->keep = 0;
m->sub = NULL;
m->wsub = NULL;
m->opaque = opaque->exec;
2025-12-23 18:21:44 +09:00
arrput(mc->sub, m);
}
}
}
2025-12-21 23:34:08 +09:00
static void menu(MwWidget handle, void* user, void* client) {
opaque_t* opaque = handle->opaque;
2025-12-23 22:29:18 +09:00
char* s = NULL;
MwMenu m = client;
if(m->opaque != NULL) {
2025-12-28 18:17:24 +09:00
char** args = MDEStringToExec(m->opaque, NULL);
2025-12-23 22:29:18 +09:00
int i;
if(fork() == 0) {
execvp(args[0], args);
_exit(-1);
}
for(i = 0; i < arrlen(args); i++) {
if(args[i] != NULL) free(args[i]);
}
arrfree(args);
}
2025-12-21 23:34:08 +09:00
if(opaque->is_opened) {
opaque->is_opened = 0;
opaque->submenu = NULL;
MwVaApply(handle,
MwNpixmap, opaque->closed,
MwNforceInverted, 0,
NULL);
}
}
static void activate(MwWidget handle, void* user, void* client) {
opaque_t* opaque = handle->opaque;
opaque->is_opened = opaque->is_opened ? 0 : 1;
if(opaque->is_opened) {
2025-12-25 05:01:19 +09:00
MwPoint p;
MwWidget fill, image;
MwRect rc;
2025-12-21 23:34:08 +09:00
MwVaApply(handle,
MwNpixmap, opaque->opened,
MwNforceInverted, 1,
NULL);
2025-12-25 05:01:19 +09:00
opaque->submenu = MwVaCreateWidget(MwSubMenuClass, "submenu", handle, 0, MwGetInteger(handle, MwNheight), 0, 0,
MwNleftPadding, 16 + MwDefaultBorderWidth(handle),
NULL);
MwSubMenuGetSize(opaque->submenu, opaque->menu, &rc);
fill = MwVaCreateWidget(MwFrameClass, "fill", opaque->submenu, MwDefaultBorderWidth(handle), MwDefaultBorderWidth(handle), 16, rc.height - MwDefaultBorderWidth(handle) * 2,
MwNbackground, opaque->stripe_color,
NULL);
image = MwVaCreateWidget(MwImageClass, "image", fill, 0, MwGetInteger(fill, MwNheight) - 96, 16, 96,
MwNpixmap, opaque->stripe,
NULL);
2025-12-21 23:34:08 +09:00
p.x = 0;
p.y = 0;
MwSubMenuAppear(opaque->submenu, opaque->menu, &p, 1);
} else {
MwVaApply(handle,
MwNpixmap, opaque->closed,
MwNforceInverted, 0,
NULL);
if(opaque->submenu != NULL) MwDestroyWidget(opaque->submenu);
}
}
2025-12-25 00:19:55 +09:00
static int sort_alphabet(const void* a, const void* b) {
2025-12-23 18:21:44 +09:00
return strcmp((*((MwMenu*)a))->name, (*((MwMenu*)b))->name);
}
2025-12-25 00:19:55 +09:00
static char* node_get_name(xl_node_t* node) {
xl_attribute_t* a = node->first_attribute;
while(a != NULL) {
if(strcmp(a->key, "Name") == 0) return a->value;
a = a->next;
}
return NULL;
}
static void menu_scan(opaque_t* opaque, xl_node_t* node) {
xl_node_t* n = node->first_child;
while(n != NULL) {
char* name = node_get_name(n);
MwMenu m = NULL;
if(n->type == XL_NODE_NODE && strcmp(n->name, "Application") == 0 && name != NULL) {
2025-12-28 17:29:00 +09:00
m = malloc(sizeof(*m));
m->name = MDEStringDuplicate(name);
m->keep = 0;
m->wsub = NULL;
m->sub = NULL;
m->opaque = NULL;
2025-12-25 00:19:55 +09:00
opaque->current = m;
MDEDirectoryScan(DATAROOTDIR "/applications", call, opaque);
#if defined(__NetBSD__)
MDEDirectoryScan("/usr/pkg/share/applications", call, opaque);
#endif
2025-12-28 17:29:00 +09:00
MDEDirectoryScan("/usr/share/applications", call, opaque);
2025-12-25 00:19:55 +09:00
} else if(n->type == XL_NODE_NODE && strcmp(n->name, "Separator") == 0) {
2025-12-28 17:29:00 +09:00
m = malloc(sizeof(*m));
m->name = MDEStringDuplicate("----");
m->keep = 0;
m->wsub = NULL;
m->sub = NULL;
m->opaque = NULL;
2025-12-25 00:19:55 +09:00
} else if(n->type == XL_NODE_NODE && strcmp(n->name, "Execute") == 0 && name != NULL) {
m = malloc(sizeof(*m));
2025-12-28 17:29:00 +09:00
m->name = MDEStringDuplicate(name);
2025-12-25 00:19:55 +09:00
m->keep = 0;
m->wsub = NULL;
m->sub = NULL;
2025-12-28 17:29:00 +09:00
m->opaque = MDEStringDuplicate(n->text == NULL ? "" : n->text);
2025-12-28 21:57:15 +09:00
} else if(n->type == XL_NODE_NODE && strcmp(n->name, "LogOff") == 0 && name != NULL) {
2025-12-28 00:36:44 +09:00
m = malloc(sizeof(*m));
2025-12-28 17:29:00 +09:00
m->name = MDEStringDuplicate(name);
2025-12-28 00:36:44 +09:00
m->keep = 0;
m->wsub = NULL;
m->sub = NULL;
m->opaque = malloc(512);
sprintf(m->opaque, "kill %ld", (long)getppid());
} else if(n->type == XL_NODE_NODE && strcmp(n->name, "ShutDown") == 0 && name != NULL) {
m = malloc(sizeof(*m));
2025-12-28 17:29:00 +09:00
m->name = MDEStringDuplicate(name);
2025-12-28 00:36:44 +09:00
m->keep = 0;
m->wsub = NULL;
m->sub = NULL;
2025-12-28 17:29:00 +09:00
m->opaque = MDEStringDuplicate(""); /* TODO */
2025-12-28 00:36:44 +09:00
} else if(n->type == XL_NODE_NODE && strcmp(n->name, "Reboot") == 0 && name != NULL) {
m = malloc(sizeof(*m));
2025-12-28 17:29:00 +09:00
m->name = MDEStringDuplicate(name);
2025-12-28 00:36:44 +09:00
m->keep = 0;
m->wsub = NULL;
m->sub = NULL;
2025-12-28 17:29:00 +09:00
m->opaque = MDEStringDuplicate(""); /* TODO */
2025-12-25 00:19:55 +09:00
}
if(m != NULL) {
arrput(opaque->menu->sub, m);
}
n = n->next;
}
}
2025-12-28 17:29:00 +09:00
static void recursive_free(MwMenu menu) {
int i;
for(i = 0; i < arrlen(menu->sub); i++) recursive_free(menu->sub[i]);
if(menu->opaque != NULL) free(menu->opaque);
if(menu->name != NULL) free(menu->name);
if(menu->wsub != NULL) MwDestroyWidget(menu->wsub);
free(menu);
}
void module_reload(MwWidget box, MwWidget user, xl_node_t* node) {
xl_node_t* n;
opaque_t* opaque = user->opaque;
int i;
2025-12-28 18:57:01 +09:00
if(opaque->submenu != NULL) {
activate(user, NULL, NULL);
}
2025-12-28 17:29:00 +09:00
if(opaque->menu != NULL) recursive_free(opaque->menu);
opaque->menu = malloc(sizeof(*opaque->menu));
opaque->menu->name = NULL;
opaque->menu->keep = 0;
opaque->menu->wsub = NULL;
opaque->menu->sub = NULL;
opaque->menu->opaque = NULL;
n = node->first_child;
while(n != NULL) {
if(n->type == XL_NODE_NODE && strcmp(n->name, "Menu") == 0) {
menu_scan(opaque, n);
} else if(n->type == XL_NODE_NODE && strcmp(n->name, "Stripe") == 0) {
xl_attribute_t* a;
if(opaque->stripe != NULL) MwLLDestroyPixmap(opaque->stripe);
opaque->stripe = MwLoadImage(box, n->text == NULL ? "" : n->text);
a = n->first_attribute;
while(a != NULL) {
if(strcmp(a->key, "Color") == 0 && a->value != NULL) {
if(opaque->stripe_color != NULL) free(opaque->stripe_color);
opaque->stripe_color = MDEStringDuplicate(a->value);
}
a = a->next;
}
}
n = n->next;
}
if(arrlen(opaque->menu->sub[0]->sub) > 0) {
qsort(opaque->menu->sub[0]->sub, arrlen(opaque->menu->sub[0]->sub), sizeof(MwMenu), sort_alphabet);
for(i = 0; i < arrlen(opaque->menu->sub[0]->sub); i++) {
qsort(opaque->menu->sub[0]->sub[i]->sub, arrlen(opaque->menu->sub[0]->sub[i]->sub), sizeof(MwMenu), sort_alphabet);
}
}
}
MwWidget module(MwWidget box, xl_node_t* node) {
2025-12-18 11:43:16 +09:00
MwLLPixmap closed = NULL;
MwLLPixmap opened = NULL;
2025-12-21 23:34:08 +09:00
MwWidget btn;
2025-12-18 11:43:16 +09:00
int w, h;
unsigned int ch;
unsigned char* rgb;
2025-12-21 23:34:08 +09:00
opaque_t* opaque = malloc(sizeof(*opaque));
2025-12-18 11:43:16 +09:00
2025-12-27 19:58:13 +09:00
if((rgb = stbi_load(ICON64DIR "/apps/mde.png", &w, &h, &ch, 4)) != NULL) {
2025-12-18 11:43:16 +09:00
int bw = MwGetInteger(box, MwNheight) - MwDefaultBorderWidth(box) * 2;
unsigned char* data = malloc(bw * bw * 4);
unsigned char* save = malloc(bw * bw * 4);
int y, x;
int max = (TriW - 1) / 2;
memset(data, 0, bw * bw * 4);
for(y = 0; y < h; y++) {
int fy = y * bw / h;
for(x = 0; x < w; x++) {
int fx = x * bw / w;
memcpy(&data[(fy * bw + fx) * 4], &rgb[(y * w + x) * 4], 4);
}
}
memcpy(save, data, bw * bw * 4);
for(y = 0; y < max + 1; y++) {
int tw = y * 2 + 1;
int ix = w - TriW + (max - y);
for(x = 0; x < tw; x++) {
data[4 * (y * bw + x + ix) + 3] = 255;
}
}
closed = MwLoadRaw(box, data, bw, bw);
memcpy(data, save, bw * bw * 4);
max = (TriW - 1) / 2;
for(y = 0; y < max + 1; y++) {
int tw = (max - y) * 2 + 1;
int ix = w - TriW + y;
for(x = 0; x < tw; x++) {
data[4 * (y * bw + x + ix) + 3] = 255;
}
}
opened = MwLoadRaw(box, data, bw, bw);
free(save);
free(data);
free(rgb);
}
2025-12-21 23:34:08 +09:00
btn = MwVaCreateWidget(MwButtonClass, "startbutton", box, 0, 0, 0, 0,
MwNfixedSize, MwGetInteger(box, MwNheight),
MwNflat, 1,
MwNpixmap, closed,
NULL);
2025-12-28 17:29:00 +09:00
btn->opaque = opaque;
2025-12-21 23:34:08 +09:00
opaque->closed = closed;
opaque->opened = opened;
opaque->submenu = NULL;
opaque->is_opened = 0;
2025-12-25 05:01:19 +09:00
opaque->stripe_color = NULL;
opaque->stripe = NULL;
2025-12-28 17:29:00 +09:00
opaque->menu = NULL;
2025-12-25 05:01:19 +09:00
2025-12-28 17:29:00 +09:00
module_reload(box, btn, node);
2025-12-21 23:34:08 +09:00
MwAddUserHandler(btn, MwNactivateHandler, activate, NULL);
MwAddUserHandler(btn, MwNmenuHandler, menu, NULL);
2025-12-28 17:29:00 +09:00
return btn;
2025-12-18 11:43:16 +09:00
}
2025-12-28 18:06:55 +09:00
void module_destroy(MwWidget box, MwWidget user) {
opaque_t* opaque = user->opaque;
MwLLDestroyPixmap(opaque->closed);
MwLLDestroyPixmap(opaque->opened);
if(opaque->stripe != NULL) MwLLDestroyPixmap(opaque->stripe);
if(opaque->stripe_color != NULL) free(opaque->stripe_color);
recursive_free(opaque->menu);
free(opaque);
MwDestroyWidget(user);
}