commit 7603358afea742ceea6cb4238b1489c6075c8660 Author: 諏訪子 Date: Tue Jan 6 21:47:05 2026 +0900 SVNからのミラー diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..f4c247e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,7 @@ +# 0.2.0 (2025/07/07) +* .desktopファイルがなければ、$PATHから実効してみる様に +* um.desktopファイルの追加 +* CLI用プログラムの場合は端末にラウンチする様に + +# 0.1.0 (2024/09/16) +* 開始 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..777a97f --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,13 @@ +Copyright © 2018-2024 by 076.moe + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD +TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR +CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, +DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS +ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e07d659 --- /dev/null +++ b/Makefile @@ -0,0 +1,97 @@ +UNAME_S != uname -s +UNAME_M != uname -m +OS = ${UNAME_S} +ARCH = ${UNAME_M} + +.if ${UNAME_S} == "OpenBSD" +OS = openbsd +.elif ${UNAME_S} == "NetBSD" +OS = netbsd +.elif ${UNAME_S} == "FreeBSD" +OS = freebsd +.elif ${UNAME_S} == "Dragonfly" +OS = dragonfly +.elif ${UNAME_S} == "Linux" +OS = linux +.endif + +.if ${UNAME_M} == "x86_64" +ARCH = amd64 +.endif + +NAME != cat main.c | grep "const char \*sofname" | awk '{print $$5}' |\ + sed "s/\"//g" | sed "s/;//" +VERSION != cat main.c | grep "const char \*version" | awk '{print $$5}' |\ + sed "s/\"//g" | sed "s/;//" + +PREFIX = /usr/local +.if ${OS} == "linux" +PREFIX = /usr +.endif + +CC = cc +FILES = main.c src/*.c + +CFLAGS = -Wall -Wextra -I/usr/include -L/usr/lib +.if ${OS} == "netbsd" +CFLAGS += -I/usr/X11R7/include -I/usr/X11R7/include/freetype2 -L/usr/X11R7/lib +.elif ${OS} == "openbsd" +CFLAGS += -I/usr/X11R6/include -I/usr/X11R6/include/freetype2 -L/usr/X11R6/lib +.elif ${OS} == "freebsd" +CFLAGS += -I/usr/local/include -I/usr/local/include/X11\ + -I /usr/local/include/freetype2\ + -L/usr/local/lib +.elif ${OS} == "linux" +CFLAGS += -I/usr/include/freetype2 +.endif + +LDFLAGS = -lc -lX11 -lXft +SLIB = -lxcb +.if ${OS} == "openbsd" +SLIB += -lfontconfig -lz -lexpat -lfreetype -lXrender -lXau -lXdmcp +.elif ${OS} == "freebsd" +SLIB += -lthr -lfontconfig -lfreetype -lXrender -lXau -lXdmcp -lexpat -lz -lbz2\ + -lpng16 -lbrotlidec -lm -lbrotlicommon +.elif ${OS} == "netbsd" +SLIB += -lfontconfig -lfreetype -lXau -lXdmcp -lgcc -lexpat -lz -lbz2 -lXrandr\ + -lXrender -lXext -lX11 +.elif ${OS} == "linux" +SLIB += -lfontconfig -lfreetype -lXrender -lXau -lXdmcp -lX11\ + -lexpat -lpng16 -lbz2 -lz\ + -lbrotlidec -lbrotlicommon +.endif + +all: + ${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -static ${LDFLAGS} ${SLIB} + strip ${NAME} + +debug: + ${CC} -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS} + +clean: + rm -rf ${NAME} + +dist: + mkdir -p ${NAME}-${VERSION} release/src + cp -R LICENSE.txt Makefile README.md CHANGELOG.md\ + main.c ${NAME}.desktop ${NAME}-${VERSION} + tar zcfv release/src/${NAME}-${VERSION}.tar.gz ${NAME}-${VERSION} + rm -rf ${NAME}-${VERSION} + +release: + mkdir -p release/bin/${VERSION}/${OS}/${ARCH} + ${CC} -O3 ${CFLAGS} -o release/bin/${VERSION}/${OS}/${ARCH}/${NAME} ${FILES}\ + -static ${LDFLAGS} ${SLIB} + strip release/bin/${VERSION}/${OS}/${ARCH}/${NAME} + +install: + mkdir -p ${DESTDIR}${PREFIX}/bin ${DESTDIR}${PREFIX}/share/applications + cp -f ${NAME} ${DESTDIR}${PREFIX}/bin + cp -f ${NAME}.desktop ${DESTDIR}${PREFIX}/share/applications + chmod 644 ${DESTDIR}${PREFIX}/share/applications/${NAME}.desktop + chmod 755 ${DESTDIR}${PREFIX}/bin/${NAME} + +uninstall: + rm -f ${DESTDIR}${PREFIX}/bin/${NAME} + +.PHONY: all debug clean dist release install uninstall diff --git a/README.md b/README.md new file mode 100644 index 0000000..c4997da --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# um +只のランチャー。 + +Xorg以外、従属ライブラリを使いません。 + +## インストールする方法 | Installation +### BSD +```sh +make +doas make install +``` + +### Linux +```sh +bmake +doas bmake install +``` + +![](scrot1.png) + +![](scrot2.png) diff --git a/fonts.dir b/fonts.dir new file mode 100644 index 0000000..573541a --- /dev/null +++ b/fonts.dir @@ -0,0 +1 @@ +0 diff --git a/fonts.scale b/fonts.scale new file mode 100644 index 0000000..573541a --- /dev/null +++ b/fonts.scale @@ -0,0 +1 @@ +0 diff --git a/main.c b/main.c new file mode 100644 index 0000000..8d8e734 --- /dev/null +++ b/main.c @@ -0,0 +1,114 @@ +#include +#include + +#include "src/control.h" +#include "src/program.h" +#include "src/display.h" +#include "src/parser.h" +#include "src/utils.h" + +XftColor color, selcolor; +Colormap colormap; +XftDraw *draw; +XftFont *font; +Visual *visual; + +const char *sofname = "um"; +const char *version = "0.2.0"; + +int main() { + Display *display; + Window window; + XEvent event; + int screen; + GC gc = NULL; + XGCValues values; + char input[MAX_NAME_LEN] = {0}; + int sel = 0; + + fetch_programs(); + + display = XOpenDisplay(NULL); + if (display == NULL) { + fprintf(stderr, "画面を開けられません。\n"); + exit(1); + } + + screen = DefaultScreen(display); + + int screen_width = DisplayWidth(display, screen); + int screen_height = DisplayHeight(display, screen); + window_width = screen_width / 4; + window_height = screen_height / 4; + int window_x = (screen_width - window_width) / 2; + int window_y = (screen_height - window_height) / 2; + + window = XCreateSimpleWindow(display, RootWindow(display, screen), + window_x, window_y, window_width, window_height, 1, BLACK, BLACK); + if (!window) { + cleanup(display, window, gc, &color, &selcolor, draw, font, colormap, visual); + } + + XSetWindowBackground(display, window, BGCOL); + + XSelectInput(display, window, + ExposureMask | KeyPressMask | StructureNotifyMask | ButtonPressMask); + + gc = XCreateGC(display, window, 0, &values); + if (!gc) { + cleanup(display, window, gc, &color, &selcolor, draw, font, colormap, visual); + fprintf(stderr, "GCを作れません。\n"); + exit(1); + } + + visual = DefaultVisual(display, screen); + + colormap = XCreateColormap(display, window, visual, AllocNone); + if (colormap == None) { + cleanup(display, window, gc, &color, &selcolor, draw, font, colormap, visual); + fprintf(stderr, "カラーマップを作れません。\n"); + exit(1); + } + + draw = XftDrawCreate(display, window, visual, colormap); + if (!draw) { + cleanup(display, window, gc, &color, &selcolor, draw, font, colormap, visual); + fprintf(stderr, "ドローを作れません。\n"); + exit(1); + } + + font = XftFontOpenName(display, screen, "Noto Sans CJK-12"); + if (!font) { + cleanup(display, window, gc, &color, &selcolor, draw, font, colormap, visual); + fprintf(stderr, "フォントの読み込みに失敗。\n"); + exit(1); + } + + if (!XftColorAllocName(display, visual, colormap, "white", &color)) { + cleanup(display, window, gc, &color, &selcolor, draw, font, colormap, visual); + fprintf(stderr, "色の役割に失敗。\n"); + } + + if (!XftColorAllocName(display, visual, colormap, "black", &selcolor)) { + cleanup(display, window, gc, &color, &selcolor, draw, font, colormap, visual); + fprintf(stderr, "選択色の役割に失敗。\n"); + } + + XMapWindow(display, window); + + while (1) { + XNextEvent(display, &event); + + if (event.type == Expose || event.type == ConfigureNotify) { + control_expose(display, window, gc, &event, input, &sel); + } else if (event.type == KeyPress) { + control_keypress(display, window, gc, &event, input, &sel); + } else if (event.type == ButtonPress) { + control_buttonpress(&event, programs, item_height); + } + } + + cleanup(display, window, gc, &color, &selcolor, draw, font, colormap, visual); + + return 0; +} diff --git a/scrot1.png b/scrot1.png new file mode 100755 index 0000000..337b71d Binary files /dev/null and b/scrot1.png differ diff --git a/scrot2.png b/scrot2.png new file mode 100755 index 0000000..f6e0d9c Binary files /dev/null and b/scrot2.png differ diff --git a/src/control.c b/src/control.c new file mode 100644 index 0000000..30b8de1 --- /dev/null +++ b/src/control.c @@ -0,0 +1,91 @@ +#include "control.h" +#include "display.h" +#include "utils.h" + +#include +#include +#include + +void control_expose( + Display *display, Window window, GC gc, XEvent *event, char *input, int *sel) { + if (event->type == ConfigureNotify) + calculate_dimensions(event->xconfigure.width, event->xconfigure.height); + filterdisplay(display, window, gc, input, *sel); +} + +void control_keypress( + Display *display, Window window, GC gc, XEvent *event, char *input, int *sel) { + char buf[32]; + KeySym keysym; + int len = XLookupString(&event->xkey, buf, sizeof(buf) - 1, &keysym, NULL); + + if (keysym == XK_Up) { + if (*sel > 0) { + (*sel)--; + if (*sel < topidx) topidx--; + } + } else if (keysym == XK_Down) { + int filteredcount = filtercount(input); + if (*sel < filteredcount - 1) { + (*sel)++; + if (*sel >= topidx + display_items) topidx++; + } + } else if (keysym == XK_Page_Up) { + if (*sel > 0) { + *sel -= display_items; + if (*sel < topidx) topidx -= display_items; + } + } else if (keysym == XK_Page_Down) { + int filteredcount = filtercount(input); + if (*sel < filteredcount - 1) { + *sel += display_items; + if (*sel >= topidx + display_items) topidx += display_items; + } + } else if (keysym == XK_BackSpace && strlen(input) > 0) { + input[strlen(input) - 1] = '\0'; + *sel = 0; + topidx = 0; + } else if (keysym == XK_Return) { + int visible_index = 0; + int found = 0; + for (int i = 0; i < programcount; i++) { + if (!strcasestr(programs[i].name, input)) continue; + if (visible_index == *sel) { + launch_program(programs[i].exec, programs[i].term); + found = 1; + break; + } + visible_index++; + } + + if (found == 0 && launch_path(input)) { + launch_program(input, "false"); + } + exit(0); + } else if (keysym == XK_Escape) { + exit(0); + } else if (len > 0 && len < MAX_NAME_LEN - 1) { + strncat(input, buf, len); + *sel = 0; + topidx = 0; + } + + filterdisplay(display, window, gc, input, *sel); +} + +void control_buttonpress(XEvent *event, Program *programs, int item_height) { + int x = event->xbutton.x; + int y = event->xbutton.y; + + for (int i = 0; i < programcount; i++) { + if ( + x >= programs[i].x && + x <= programs[i].x + programs[i].width && + y >= programs[i].y - item_height && + y <= programs[i].y + ) { + launch_program(programs[i].exec, programs[i].term); + break; + } + } +} diff --git a/src/control.h b/src/control.h new file mode 100644 index 0000000..a9f18fc --- /dev/null +++ b/src/control.h @@ -0,0 +1,12 @@ +#ifndef CONTROL_H +#define CONTROL_H + +#include "program.h" + +void control_expose( + Display *display, Window window, GC gc, XEvent *event, char *input, int *sel); +void control_keypress( + Display *display, Window window, GC gc, XEvent *event, char *input, int *sel); +void control_buttonpress(XEvent *event, Program *programs, int item_height); + +#endif diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..bee591b --- /dev/null +++ b/src/display.c @@ -0,0 +1,46 @@ +#include "display.h" + +#include + +int topidx = 0; + +void drawtext( + Display *display, Window window, GC gc, int x, int y, const char *text, int sel) { + if (sel) { + XSetForeground(display, gc, BLACK); + XFillRectangle( + display, window, gc, 0, y - item_height + 5, window_width, item_height); + XSetForeground(display, gc, BLACK); + } else { + XSetForeground(display, gc, WHITE); + } + + XftDrawStringUtf8(draw, &color, font, x, y, (const FcChar8 *)text, strlen(text)); +} + +void filterdisplay( + Display *display, + Window window, + GC gc, + const char *input, + int sel +) { + XClearWindow(display, window); + + // 検索ボックス + drawtext(display, window, gc, 10, item_height, input, 0); + + int y = item_height * 2; + int idx = 0; + + for (int i = 0; i < programcount; i++) { + if (!strcasestr(programs[i].name, input) && !strcasestr(programs[i].keys, input)) + continue; + + if (idx >= topidx && idx < topidx + display_items) { + drawtext(display, window, gc, 10, y, programs[i].name, idx == sel); + y += item_height; + } + idx++; + } +} diff --git a/src/display.h b/src/display.h new file mode 100644 index 0000000..7dafc84 --- /dev/null +++ b/src/display.h @@ -0,0 +1,21 @@ +#ifndef DISPLAY_H +#define DISPLAY_H + +#include "program.h" + +#include +#include + +void drawtext( + Display *display, Window window, GC gc, int x, int y, const char *text, int sel); +void filterdisplay( + Display *display, Window window, GC gc, const char *input, int sel); + +extern XftColor color, selcolor; +extern Colormap colormap; +extern XftDraw *draw; +extern XftFont *font; +extern Visual *visual; +extern int topidx; + +#endif diff --git a/src/parser.c b/src/parser.c new file mode 100644 index 0000000..56351f0 --- /dev/null +++ b/src/parser.c @@ -0,0 +1,138 @@ +#include "parser.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include + +bool foundName = false; +bool foundKey = false; +bool foundExec = false; +bool isDesktopEntry = false; + +void parse_name(char *line, Program *program) { + if (foundName) return; + if (strncmp(line, "Name[", 5) == 0) { + char *locale = line + 5; + char *end = strchr(locale, ']'); + if (!end) return; + *end = '\0'; + if (strncmp(locale, getenv("LANG"), 2) != 0) return; + if (!isdup(end + 2)) { + strncpy(program->name, end + 2, MAX_NAME_LEN - 1); + program->name[strcspn(program->name, "\n")] = '\0'; + } + add_to_dup(end + 2); + foundName = true; + } else { + if (!isdup(line + 5)) { + strncpy(program->name, line + 5, MAX_NAME_LEN - 1); + program->name[strcspn(program->name, "\n")] = '\0'; + } + add_to_dup(line + 5); + foundName = true; + } +} + +void parse_keywords(char *line, Program *program) { + if (foundKey) return; + size_t len = strnlen(line + 9, MAX_NAME_LEN - 1); + strncpy(program->keys, line + 9, len); + program->keys[len] = '\0'; + + program->keys[strcspn(program->keys, "\n")] = '\0'; + foundKey = true; +} + +void parse_exec(char *line, Program *program) { + if (foundExec) return; + size_t len = strnlen(line + 5, MAX_NAME_LEN - 1); + strncpy(program->exec, line + 5, len); + program->exec[strcspn(program->exec, "\n")] = '\0'; + + char *p = program->exec; + while ((p = strpbrk(p, "%")) != NULL) { + *p = '\0'; + + size_t rspace = MAX_NAME_LEN - strlen(program->exec) - 1; + if (rspace > 0) { + strncat(program->exec, p + 2, rspace); + } + } + foundExec = true; +} + +void parse_term(char *line, Program *program) { + size_t len = strnlen(line + 9, MAX_NAME_LEN - 1); + strncpy(program->term, line + 9, len); + program->term[strcspn(program->term, "\n")] = '\0'; + + char *p = program->term; + + while ((p = strpbrk(p, "%")) != NULL) { + *p = '\0'; + + size_t rspace = MAX_NAME_LEN - strlen(program->term) - 1; + if (rspace > 0) { + strncat(program->term, p + 2, rspace); + } + } +} + +void parse_desktop_file(const char *filepath) { + FILE *file = fopen(filepath, "r"); + if (!file) return; + + char line[MAX_NAME_LEN * 2]; + Program program = {0}; + + while (fgets(line, sizeof(line), file)) { + if (strncmp(line, "Name=", 5) == 0 || strncmp(line, "Name[", 5) == 0) { + parse_name(line, &program); + } else if (strncmp(line, "Keywords=", 9) == 0) { + parse_keywords(line, &program); + } else if (strncmp(line, "Exec=", 5) == 0) { + parse_exec(line, &program); + } else if (strncmp(line, "Terminal=", 9) == 0) { + parse_term(line, &program); + } + } + + fclose(file); + + if (program.name[0] && program.exec[0]) { + programs[programcount++] = program; + } +} + +void scan_desktop_files(const char *directory) { + DIR *dir = opendir(directory); + if (!dir) return; + + struct dirent *entry; + while ((entry = readdir(dir)) != NULL) { + if (strcasestr(entry->d_name, ".desktop")) { + size_t filepath_len = strlen(directory) + strlen(entry->d_name) + 2; + char *filepath = malloc(filepath_len); + if (!filepath) { + closedir(dir); + return; + } + + snprintf(filepath, filepath_len, "%s/%s", directory, entry->d_name); + if (isdis(filepath)) parse_desktop_file(filepath); + + isDesktopEntry = false; + foundName = false; + foundKey = false; + foundExec = false; + + free(filepath); + } + } + + closedir(dir); +} diff --git a/src/parser.h b/src/parser.h new file mode 100644 index 0000000..e5a6842 --- /dev/null +++ b/src/parser.h @@ -0,0 +1,13 @@ +#ifndef PARSER_H +#define PARSER_H + +#include "program.h" + +void parse_name(char *line, Program *program); +void parse_keywords(char *line, Program *program); +void parse_exec(char *line, Program *program); +void parse_term(char *line, Program *program); +void parse_desktop_file(const char *filepath); +void scan_desktop_files(const char *directory); + +#endif diff --git a/src/program.c b/src/program.c new file mode 100644 index 0000000..1af8b47 --- /dev/null +++ b/src/program.c @@ -0,0 +1,70 @@ +#include "program.h" +#include "parser.h" + +#include +#include + +#include +#include +#include +#include + +Program programs[MAX_ITEMS]; +int programcount = 0; +int window_width = 300; +int window_height = 240; +int item_height = 20; +int display_items = 10; + +void fetch_programs() { + char *xdg_data_home = getenv("XDG_DATA_HOME"); + if (!xdg_data_home) { + xdg_data_home = getenv("HOME"); + if (xdg_data_home) { + char path[MAX_NAME_LEN]; + snprintf(path, sizeof(path), "%s/.local/share/applications", xdg_data_home); + scan_desktop_files(path); + } + } else { + char path[MAX_NAME_LEN]; + snprintf(path, sizeof(path), "%s/applications", xdg_data_home); + scan_desktop_files(path); + } + + scan_desktop_files("/usr/share/applications"); + scan_desktop_files("/usr/local/share/applications"); +#if defined(__NetBSD__) + scan_desktop_files("/usr/pkg/share/applications"); +#endif +} + +void launch_program(const char *exec, const char *term) { + if (fork() == 0) { + setsid(); + if (strncmp(term, "true", 4) == 0) { + char *terminal = getenv("TERMINAL"); + if (!terminal) terminal = "xterm"; + execlp(terminal, terminal, "-e", exec, NULL); + } else { + execl("/bin/sh", "sh", "-c", exec, NULL); + } + exit(0); + } +} + +int filtercount(const char *input) { + int count = 0; + + for (int i = 0; i < programcount; i++) { + if (strcasestr(programs[i].name, input) || strcasestr(programs[i].keys, input)) + count++; + } + + return count; +} + +void calculate_dimensions(int win_width, int win_height) { + window_width = win_width; + window_height = win_height; + item_height = win_height / (display_items + 2); +} diff --git a/src/program.h b/src/program.h new file mode 100644 index 0000000..185a777 --- /dev/null +++ b/src/program.h @@ -0,0 +1,35 @@ +#ifndef PROGRAM_H +#define PROGRAM_H + +#include +#include + +#define MAX_ITEMS 512 +#define MAX_NAME_LEN 256 +#define MAX_LINE_LENGTH 256 + +#define WHITE 0xfcfcfc +#define BLACK 0x120f12 +#define BGCOL 0x550f75 + +typedef struct { + char name[MAX_NAME_LEN]; + char keys[MAX_NAME_LEN]; + char exec[MAX_NAME_LEN]; + char term[MAX_NAME_LEN]; + int x, y, width, height; +} Program; + +void fetch_programs(); +void launch_program(const char *exec, const char *term); +int filtercount(const char *input); +void calculate_dimensions(int win_width, int win_height); + +extern Program programs[MAX_ITEMS]; +extern int programcount; +extern int window_width; +extern int window_height; +extern int item_height; +extern int display_items; + +#endif diff --git a/src/utils.c b/src/utils.c new file mode 100644 index 0000000..95d5f14 --- /dev/null +++ b/src/utils.c @@ -0,0 +1,98 @@ +#include "utils.h" + +#include +#include +#include +#include + +#if defined(__linux__) +#include +#include +#endif + +Dup dupn[MAX_NAME_LEN * 2]; +int dupcnt = 0; + +#if defined(__linux__) +#include + +char *strcasestr(const char *haystack, const char *needle) { + size_t needle_len = strlen(needle); + if (needle_len == 0) { + return (char *)haystack; + } + + while (*haystack) { + if (strncasecmp(haystack, needle, needle_len) == 0) { + return (char *)haystack; + } + haystack++; + } + return NULL; +} +#endif + +int isdup(const char *name) { + for (int i = 0; i < dupcnt; i++) { + if (strncmp(dupn[i].name, name, strlen(name)) == 0) return 1; + } + + return 0; +} + +int isdis(const char *filepath) { + FILE *file = fopen(filepath, "r"); + if (!file) return 0; + + char line[MAX_LINE_LENGTH]; + while (fgets(line, sizeof(line), file)) { + if (strstr(line, "NoDisplay=true")) { + fclose(file); + return 0; + } + } + + fclose(file); + return 1; +} + +void add_to_dup(const char *name) { + if (dupcnt < (MAX_NAME_LEN * 2)) { + strncpy(dupn[dupcnt].name, name, MAX_NAME_LEN - 1); + dupn[dupcnt].name[MAX_NAME_LEN - 1] = '\0'; + dupcnt++; + } +} + +int launch_path(const char *exec) { + char *path = getenv("PATH"); + if (!path) return 1; + + char *pdup = strdup(path); + char *dir = strtok(pdup, ":"); + while (dir != NULL) { + char fp[1024]; + snprintf(fp, sizeof(fp), "%s/%s", dir, exec); + + if (access(fp, X_OK) == 0) { + free(pdup); + return 1; + } + dir = strtok(NULL, ":"); + } + + free(pdup); + return 0; +} + +void cleanup(Display *display, Window window, GC gc, XftColor *color, + XftColor *selcolor, XftDraw *draw, XftFont *font, + Colormap colormap, Visual *visual) { + if (color) XftColorFree(display, visual, colormap, color); + if (selcolor) XftColorFree(display, visual, colormap, selcolor); + if (font) XftFontClose(display, font); + if (draw) XftDrawDestroy(draw); + if (gc) XFreeGC(display, gc); + if (window) XDestroyWindow(display, window); + if (display) XCloseDisplay(display); +} diff --git a/src/utils.h b/src/utils.h new file mode 100644 index 0000000..033064a --- /dev/null +++ b/src/utils.h @@ -0,0 +1,28 @@ +#ifndef UTILS_H +#define UTILS_H + +#include "program.h" + +#include +#include + +typedef struct { + char name[MAX_NAME_LEN]; +} Dup; + +extern Dup dupn[MAX_NAME_LEN * 2]; +extern int dupcnt; + +#if defined(__linux__) +char *strcasestr(const char *haystack, const char *needle); +#endif + +int isdup(const char *name); +int isdis(const char *filepath); +void add_to_dup(const char *name); +int launch_path(const char *exec); +void cleanup(Display *display, Window window, GC gc, + XftColor *color, XftColor *selcolor, XftDraw *draw, XftFont *font, + Colormap colormap, Visual *visual); + +#endif diff --git a/um.desktop b/um.desktop new file mode 100644 index 0000000..ef88097 --- /dev/null +++ b/um.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=um +GenericName=Just launcher +GenericName[ja]=只のラウンチャー +Exec=um %F +StartupNotify=true +Terminal=false +Type=Application +Categories=Application;Utility;System;DeskopUtility;