SVNからのミラー
This commit is contained in:
7
CHANGELOG.md
Normal file
7
CHANGELOG.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# 0.2.0 (2025/07/07)
|
||||||
|
* .desktopファイルがなければ、$PATHから実効してみる様に
|
||||||
|
* um.desktopファイルの追加
|
||||||
|
* CLI用プログラムの場合は端末にラウンチする様に
|
||||||
|
|
||||||
|
# 0.1.0 (2024/09/16)
|
||||||
|
* 開始
|
||||||
13
LICENSE.txt
Normal file
13
LICENSE.txt
Normal file
@@ -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.
|
||||||
97
Makefile
Normal file
97
Makefile
Normal file
@@ -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
|
||||||
21
README.md
Normal file
21
README.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# um
|
||||||
|
只のランチャー。
|
||||||
|
|
||||||
|
Xorg以外、従属ライブラリを使いません。
|
||||||
|
|
||||||
|
## インストールする方法 | Installation
|
||||||
|
### BSD
|
||||||
|
```sh
|
||||||
|
make
|
||||||
|
doas make install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
```sh
|
||||||
|
bmake
|
||||||
|
doas bmake install
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|

|
||||||
1
fonts.scale
Normal file
1
fonts.scale
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
114
main.c
Normal file
114
main.c
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#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;
|
||||||
|
}
|
||||||
BIN
scrot1.png
Executable file
BIN
scrot1.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 401 KiB |
BIN
scrot2.png
Executable file
BIN
scrot2.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 402 KiB |
91
src/control.c
Normal file
91
src/control.c
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
#include "control.h"
|
||||||
|
#include "display.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/control.h
Normal file
12
src/control.h
Normal file
@@ -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
|
||||||
46
src/display.c
Normal file
46
src/display.c
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "display.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
src/display.h
Normal file
21
src/display.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
#ifndef DISPLAY_H
|
||||||
|
#define DISPLAY_H
|
||||||
|
|
||||||
|
#include "program.h"
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xft/Xft.h>
|
||||||
|
|
||||||
|
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
|
||||||
138
src/parser.c
Normal file
138
src/parser.c
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
#include "parser.h"
|
||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
13
src/parser.h
Normal file
13
src/parser.h
Normal file
@@ -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
|
||||||
70
src/program.c
Normal file
70
src/program.c
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
#include "program.h"
|
||||||
|
#include "parser.h"
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xft/Xft.h>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
35
src/program.h
Normal file
35
src/program.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
#ifndef PROGRAM_H
|
||||||
|
#define PROGRAM_H
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xft/Xft.h>
|
||||||
|
|
||||||
|
#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
|
||||||
98
src/utils.c
Normal file
98
src/utils.c
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
Dup dupn[MAX_NAME_LEN * 2];
|
||||||
|
int dupcnt = 0;
|
||||||
|
|
||||||
|
#if defined(__linux__)
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
28
src/utils.h
Normal file
28
src/utils.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef UTILS_H
|
||||||
|
#define UTILS_H
|
||||||
|
|
||||||
|
#include "program.h"
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xft/Xft.h>
|
||||||
|
|
||||||
|
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
|
||||||
10
um.desktop
Normal file
10
um.desktop
Normal file
@@ -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;
|
||||||
Reference in New Issue
Block a user