最初コミット
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
ucalc
|
||||||
|
*.o
|
||||||
|
*.core
|
||||||
2
CHANGELOG.md
Normal file
2
CHANGELOG.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# 0.0.0
|
||||||
|
* 標準電卓
|
||||||
0
LICENSE.md
Normal file
0
LICENSE.md
Normal file
57
Makefile
Normal file
57
Makefile
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
UNAME_S != uname -s
|
||||||
|
OS = ${UNAME_S}
|
||||||
|
|
||||||
|
.if ${UNAME_S} == "OpenBSD"
|
||||||
|
OS = openbsd
|
||||||
|
.elif ${UNAME_S} == "FreeBSD"
|
||||||
|
OS = freebsd
|
||||||
|
.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
|
||||||
|
|
||||||
|
CC = cc
|
||||||
|
FILES = main.c src/*.c
|
||||||
|
# FILES = main.c
|
||||||
|
|
||||||
|
CFLAGS = -Wall -Wextra \
|
||||||
|
-I./dep/include -I/usr/include -I/usr/local/include -I/usr/X11R6/include \
|
||||||
|
-L./dep/lib/${OS} -L/usr/lib -L/usr/local/lib -L/usr/X11R6/lib \
|
||||||
|
-I/usr/X11R6/include/freetype2 -I/usr/local/include/freetype2
|
||||||
|
|
||||||
|
LDFLAGS = -lc -lX11 -lXft -lsys
|
||||||
|
SLIB = -lxcb -lthr -lfontconfig -lfreetype -lXrender -lXau -lXdmcp -lexpat -lintl \
|
||||||
|
-lbz2 -lpng16 -lbrotlidec -lz -lm -lbrotlicommon
|
||||||
|
|
||||||
|
all: debug
|
||||||
|
|
||||||
|
debug:
|
||||||
|
${CC} -O0 -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS}
|
||||||
|
lldb -o run ${NAME}
|
||||||
|
|
||||||
|
develop:
|
||||||
|
${CC} -O3 -g ${CFLAGS} -o ${NAME} ${FILES} ${LDFLAGS} ${SLIB}
|
||||||
|
./${NAME}
|
||||||
|
|
||||||
|
release:
|
||||||
|
${CC} -O3 ${CFLAGS} -o ${NAME} ${FILES} -static ${LDFLAGS} ${SLIB}
|
||||||
|
strip ${NAME}
|
||||||
|
./${NAME}
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf ${NAME} *.core *.o
|
||||||
|
|
||||||
|
install:
|
||||||
|
cp ${NAME} ${PREFIX}/bin
|
||||||
|
chmod +x ${PREFIX}/bin/${NAME}
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
rm -rf ${PREFIX}/bin/${NAME}
|
||||||
|
|
||||||
|
dist:
|
||||||
|
|
||||||
|
.PHONY: all debug develop release clean install uninstall dist
|
||||||
20
README.md
Normal file
20
README.md
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
# Unix Calc
|
||||||
|
|
||||||
|
XLibre向け電卓。
|
||||||
|
Xorg以外、従属ライブラリを使いません。
|
||||||
|
|
||||||
|
## インストールする方法 | Installation
|
||||||
|
### BSD
|
||||||
|
```sh
|
||||||
|
make
|
||||||
|
doas make install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Linux
|
||||||
|
```sh
|
||||||
|
make
|
||||||
|
doas bmake install
|
||||||
|
```
|
||||||
|
|
||||||
|
\
|
||||||
|

|
||||||
171
main.c
Normal file
171
main.c
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <X11/Xatom.h>
|
||||||
|
#include "src/utils.h"
|
||||||
|
#include "src/control.h"
|
||||||
|
|
||||||
|
XftColor color, btncolor;
|
||||||
|
Colormap colormap;
|
||||||
|
XftFont *font;
|
||||||
|
XftFont *prbfont;
|
||||||
|
XftFont *disfont;
|
||||||
|
Visual *visual;
|
||||||
|
|
||||||
|
const char *sofname = "ucalc";
|
||||||
|
const char *version = "0.0.0";
|
||||||
|
const char *disname = "Unix Calc";
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Display *display;
|
||||||
|
Window window;
|
||||||
|
XEvent event;
|
||||||
|
int screen;
|
||||||
|
GC gc = NULL;
|
||||||
|
XGCValues values;
|
||||||
|
|
||||||
|
display = XOpenDisplay(NULL);
|
||||||
|
if (display == NULL) {
|
||||||
|
fprintf(stderr, "画面を開けられません。\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
screen = DefaultScreen(display);
|
||||||
|
|
||||||
|
int sw = DisplayWidth(display, screen);
|
||||||
|
int sh = DisplayHeight(display, screen);
|
||||||
|
int window_x = (sw - window_width) / 3;
|
||||||
|
int window_y = (sh - window_height) / 2;
|
||||||
|
|
||||||
|
window = XCreateSimpleWindow(display, RootWindow(display, screen),
|
||||||
|
window_x, window_y, window_width, window_height, 1, BTCOL, BGCOL);
|
||||||
|
if (!window) {
|
||||||
|
cleanup(display, window, gc, &color, &btncolor, font, disfont, prbfont, colormap, visual, backbuf);
|
||||||
|
fprintf(stderr, "ウィンドウを作成に失敗。\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
backbuf = XCreatePixmap(display, window, window_width, window_height, DefaultDepth(display, screen));
|
||||||
|
|
||||||
|
Atom net_wm_window_type = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
|
||||||
|
Atom dialog = XInternAtom(display, "_NET_WM_WINDOW_TYPE_DIALOG", False);
|
||||||
|
|
||||||
|
XChangeProperty(display, window, net_wm_window_type, XA_ATOM, 32,
|
||||||
|
PropModeReplace, (unsigned char *)&dialog, 1);
|
||||||
|
|
||||||
|
XStoreName(display, window, disname);
|
||||||
|
Atom net_wm_name = XInternAtom(display, "_NET_WM_NAME", False);
|
||||||
|
char displayname[16];
|
||||||
|
snprintf(displayname, 16, "%s %s", disname, version);
|
||||||
|
XChangeProperty(display, window, net_wm_name,
|
||||||
|
XInternAtom(display, "UTF8_STRING", False), 8,
|
||||||
|
PropModeReplace, (unsigned char *)displayname, strlen(displayname));
|
||||||
|
|
||||||
|
XClassHint *classHint = XAllocClassHint();
|
||||||
|
if (classHint) {
|
||||||
|
classHint->res_name = "unixcalc";
|
||||||
|
classHint->res_class = "UnixCalc";
|
||||||
|
XSetClassHint(display, window, classHint);
|
||||||
|
XFree(classHint);
|
||||||
|
}
|
||||||
|
|
||||||
|
XSetWindowBackground(display, window, BGCOL);
|
||||||
|
|
||||||
|
XSelectInput(display, window,
|
||||||
|
ExposureMask
|
||||||
|
| ButtonPressMask
|
||||||
|
| ButtonReleaseMask
|
||||||
|
| KeyPressMask
|
||||||
|
// | PointerMotionMask
|
||||||
|
// | ButtonMotionMask
|
||||||
|
// | StructureNotifyMask
|
||||||
|
);
|
||||||
|
|
||||||
|
gc = XCreateGC(display, window, 0, &values);
|
||||||
|
if (!gc) {
|
||||||
|
cleanup(display, window, gc, &color, &btncolor, font, disfont, prbfont, colormap, visual, backbuf);
|
||||||
|
fprintf(stderr, "GCを作成に失敗。\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
visual = DefaultVisual(display, screen);
|
||||||
|
|
||||||
|
colormap = XCreateColormap(display, window, visual, AllocNone);
|
||||||
|
if (colormap == None) {
|
||||||
|
cleanup(display, window, gc, &color, &btncolor, font, disfont, prbfont, colormap, visual, backbuf);
|
||||||
|
fprintf(stderr, "カラーマップを作成に失敗。\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
font = XftFontOpenName(display, screen, "Noto Sans CJK-12");
|
||||||
|
if (!font) {
|
||||||
|
cleanup(display, window, gc, &color, &btncolor, font, disfont, prbfont, colormap, visual, backbuf);
|
||||||
|
fprintf(stderr, "フォントの読み込みに失敗。\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
prbfont = XftFontOpenName(display, screen, "Noto Sans CJK-24");
|
||||||
|
if (!prbfont) {
|
||||||
|
cleanup(display, window, gc, &color, &btncolor, font, disfont, prbfont, colormap, visual, backbuf);
|
||||||
|
fprintf(stderr, "問題フォントの読み込みに失敗。\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
disfont = XftFontOpenName(display, screen, "Noto Sans CJK-72");
|
||||||
|
if (!disfont) {
|
||||||
|
cleanup(display, window, gc, &color, &btncolor, font, disfont, prbfont, colormap, visual, backbuf);
|
||||||
|
fprintf(stderr, "解決フォントの読み込みに失敗。\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!XftColorAllocName(display, visual, colormap, "#232020", &color)) {
|
||||||
|
cleanup(display, window, gc, &color, &btncolor, font, disfont, prbfont, colormap, visual, backbuf);
|
||||||
|
fprintf(stderr, "色の役割に失敗。\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
XMapWindow(display, window);
|
||||||
|
{
|
||||||
|
XWindowAttributes attr;
|
||||||
|
XGetWindowAttributes(display, window, &attr);
|
||||||
|
XEvent fake = { .type = Expose };
|
||||||
|
fake.xexpose.window = window;
|
||||||
|
fake.xexpose.width = attr.width;
|
||||||
|
fake.xexpose.height = attr.height;
|
||||||
|
control_expose(display, window, gc, &event, &color, font, disfont, prbfont);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (isrunning) {
|
||||||
|
XNextEvent(display, &event);
|
||||||
|
|
||||||
|
switch (event.type) {
|
||||||
|
case Expose:
|
||||||
|
case ConfigureNotify:
|
||||||
|
XClearWindow(display, window);
|
||||||
|
control_expose(display, window, gc, &event, &color, font, disfont, prbfont);
|
||||||
|
break;
|
||||||
|
case ButtonPress:
|
||||||
|
if (event.xbutton.button == Button1) {
|
||||||
|
handle_button_press(display, gc, event.xbutton.x, event.xbutton.y, &color, font);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ButtonRelease:
|
||||||
|
if (event.xbutton.button == Button1) {
|
||||||
|
handle_button_release(display, window, gc, event.xbutton.x, event.xbutton.y, &color, font, disfont, prbfont);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KeyPress:
|
||||||
|
handle_key_press(display, window, gc, &event, &color, font, disfont, prbfont);
|
||||||
|
break;
|
||||||
|
// case MotionNotify:
|
||||||
|
// handle_mouse_hover(display, window, gc, &event, &color, font);
|
||||||
|
// break;
|
||||||
|
case ClientMessage:
|
||||||
|
// WM_DELETE_WINDOW
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup(display, window, gc, &color, &btncolor, font, disfont, prbfont, colormap, visual, backbuf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
BIN
scrot1.png
Normal file
BIN
scrot1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
BIN
scrot2.png
Normal file
BIN
scrot2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 82 KiB |
389
src/control.c
Normal file
389
src/control.c
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
#include <X11/keysym.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include "control.h"
|
||||||
|
#include "display.h"
|
||||||
|
#include "program.h"
|
||||||
|
|
||||||
|
static const char *btn_labels[][10] = {
|
||||||
|
{ NULL }, // 数字表示
|
||||||
|
{ "C", "±", "%", "÷" },
|
||||||
|
{ "7", "8", "9", "×" },
|
||||||
|
{ "4", "5", "6", "-" },
|
||||||
|
{ "1", "2", "3", "+" },
|
||||||
|
{ "0", ".", "=", "<" },
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NUM_ROWS 6
|
||||||
|
#define NUM_COLS 6
|
||||||
|
|
||||||
|
static SuwaButton *find_button_at(int mx, int my) {
|
||||||
|
static SuwaButton found = {0};
|
||||||
|
|
||||||
|
int btn_w = 93;
|
||||||
|
int btn_h = 60;
|
||||||
|
int padding = 2;
|
||||||
|
|
||||||
|
int row = -1;
|
||||||
|
int col = -1;
|
||||||
|
|
||||||
|
for (int r = 1; r < NUM_ROWS; ++r) {
|
||||||
|
int y_start = 224 + (r - 1) * btn_h + padding;
|
||||||
|
if (my >= y_start && my < y_start + btn_h) {
|
||||||
|
row = r;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (row < 0) return NULL;
|
||||||
|
|
||||||
|
for (int c = 0; c < NUM_COLS; ++c) {
|
||||||
|
int x_start = padding + c * (btn_w + padding);
|
||||||
|
if (mx >= x_start && mx < x_start + btn_w) {
|
||||||
|
col = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (col < 0) return NULL;
|
||||||
|
|
||||||
|
found.x = padding + col * (btn_w + padding);
|
||||||
|
found.y = 224 + (row - 1) * (btn_h + padding);
|
||||||
|
found.width = btn_w;
|
||||||
|
found.height = btn_h;
|
||||||
|
found.label = btn_labels[row][col];
|
||||||
|
found.pressed = 1;
|
||||||
|
|
||||||
|
return &found;
|
||||||
|
}
|
||||||
|
|
||||||
|
void append_to_input(char c) {
|
||||||
|
if ((unsigned long)input_pos >= sizeof(curinput) - 2) return;
|
||||||
|
strcpy(displayprb, "");
|
||||||
|
curinput[input_pos++] = c;
|
||||||
|
curinput[input_pos] = '\0';
|
||||||
|
|
||||||
|
strncpy(displaytxt, curinput, sizeof(displaytxt) - 1);
|
||||||
|
displaytxt[sizeof(displaytxt) - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear_calculator(void) {
|
||||||
|
strcpy(displayprb, "");
|
||||||
|
curinput[0] = '\0';
|
||||||
|
input_pos = 0;
|
||||||
|
strcpy(displaytxt, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
double evaluate_simple(const char *expr) {
|
||||||
|
double res = 0.0;
|
||||||
|
double cur = 0.0;
|
||||||
|
char op = '+';
|
||||||
|
int i = 0;
|
||||||
|
snprintf(displayprb, sizeof(displayprb), "%s=", expr);
|
||||||
|
|
||||||
|
while (expr[i]) {
|
||||||
|
if (expr[i] == ' ') { i++; continue; }
|
||||||
|
|
||||||
|
if (isdigit(expr[i]) || expr[i] == '.') {
|
||||||
|
char numbuf[32];
|
||||||
|
int j = 0;
|
||||||
|
while (isdigit(expr[i]) || expr[i] == '.') {
|
||||||
|
if (j < 31) numbuf[j++] = expr[i];
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
numbuf[j] = '\0';
|
||||||
|
cur = atof(numbuf);
|
||||||
|
} else {
|
||||||
|
if (op == '+') res += cur;
|
||||||
|
else if (op == '-') res -= cur;
|
||||||
|
else if (op == '*') res *= cur;
|
||||||
|
else if (op == '/') {
|
||||||
|
if (cur == 0) return NAN;
|
||||||
|
res /= cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
op = expr[i];
|
||||||
|
++i;
|
||||||
|
cur = 0.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == '+') res += cur;
|
||||||
|
else if (op == '-') res -= cur;
|
||||||
|
else if (op == '*') res *= cur;
|
||||||
|
else if (op == '/') {
|
||||||
|
if (cur == 0) return NAN;
|
||||||
|
res /= cur;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void control_expose(Display *dpy, Window wnd, GC gc, XEvent *e, XftColor *color, XftFont *f, XftFont *df, XftFont *pf) {
|
||||||
|
(void)color;
|
||||||
|
if (e->type != Expose && e->type != ConfigureNotify) return;
|
||||||
|
|
||||||
|
XWindowAttributes attr;
|
||||||
|
XGetWindowAttributes(dpy, wnd, &attr);
|
||||||
|
int w = attr.width;
|
||||||
|
int h = attr.height;
|
||||||
|
|
||||||
|
if (backbuf != None) {
|
||||||
|
XFreePixmap(dpy, backbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
backbuf = XCreatePixmap(dpy, wnd, w, h, DefaultDepth(dpy, DefaultScreen(dpy)));
|
||||||
|
|
||||||
|
if (backbuf == None) {
|
||||||
|
backbuf = wnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
XftDraw *backdraw = XftDrawCreate(dpy, backbuf,
|
||||||
|
DefaultVisual(dpy, DefaultScreen(dpy)), DefaultColormap(dpy, DefaultScreen(dpy)));
|
||||||
|
if (!backdraw) {
|
||||||
|
fprintf(stderr, "Pixmap向けXftDrawの作成に失敗。\n");
|
||||||
|
XFreePixmap(dpy, backbuf);
|
||||||
|
backbuf = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XSetForeground(dpy, gc, BGCOL);
|
||||||
|
XFillRectangle(dpy, backbuf, gc, 0, 0, w, h);
|
||||||
|
|
||||||
|
// 出力
|
||||||
|
if (displaytxt[0] != '\0' && df) {
|
||||||
|
const FcChar8 *text = (const FcChar8 *)displaytxt;
|
||||||
|
int len = strlen((const char *)text);
|
||||||
|
|
||||||
|
XGlyphInfo extents;
|
||||||
|
XftTextExtentsUtf8(dpy, df, text, len, &extents);
|
||||||
|
|
||||||
|
int tx = w - 20 - extents.xOff;
|
||||||
|
int ty = 160;
|
||||||
|
|
||||||
|
XftColor discol;
|
||||||
|
XftColorAllocName(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
|
||||||
|
DefaultColormap(dpy, DefaultScreen(dpy)),
|
||||||
|
"#ee4030", &discol);
|
||||||
|
|
||||||
|
XftDrawStringUtf8(backdraw, &discol, df, tx, ty, text, len);
|
||||||
|
|
||||||
|
XftColorFree(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
|
||||||
|
DefaultColormap(dpy, DefaultScreen(dpy)), &discol);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayprb[0] != '\0' && pf) {
|
||||||
|
const FcChar8 *text = (const FcChar8 *)displayprb;
|
||||||
|
int len = strlen((const char *)text);
|
||||||
|
|
||||||
|
XGlyphInfo extents;
|
||||||
|
XftTextExtentsUtf8(dpy, pf, text, len, &extents);
|
||||||
|
|
||||||
|
int tx = w - 20 - extents.xOff;
|
||||||
|
int ty = 80;
|
||||||
|
|
||||||
|
XftColor discol;
|
||||||
|
XftColorAllocName(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
|
||||||
|
DefaultColormap(dpy, DefaultScreen(dpy)),
|
||||||
|
"#b61729", &discol);
|
||||||
|
|
||||||
|
XftDrawStringUtf8(backdraw, &discol, pf, tx, ty, text, len);
|
||||||
|
|
||||||
|
XftColorFree(dpy, DefaultVisual(dpy, DefaultScreen(dpy)),
|
||||||
|
DefaultColormap(dpy, DefaultScreen(dpy)), &discol);
|
||||||
|
}
|
||||||
|
|
||||||
|
int width = 93;
|
||||||
|
int height = 60;
|
||||||
|
int padding = 2;
|
||||||
|
printf("ウィンドウ: (%dx%d)\n", attr.width, attr.height);
|
||||||
|
|
||||||
|
for (int row = 0; row < 6; ++row) {
|
||||||
|
int y = 162 + row * (height + padding);
|
||||||
|
if (y + height > h) break;
|
||||||
|
|
||||||
|
int cols = 4;
|
||||||
|
|
||||||
|
for (int col = 0; col < cols; ++col) {
|
||||||
|
const char *label = btn_labels[row][col];
|
||||||
|
if (!label) continue;
|
||||||
|
|
||||||
|
int x = padding + col * (width + padding);
|
||||||
|
|
||||||
|
SuwaButton btn;
|
||||||
|
btn.x = x;
|
||||||
|
btn.y = y;
|
||||||
|
btn.width = width;
|
||||||
|
btn.height = height;
|
||||||
|
btn.label = label;
|
||||||
|
btn.pressed = 0;
|
||||||
|
|
||||||
|
drawbuttons(dpy, backbuf, gc, &btn, backdraw, color, f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XCopyArea(dpy, backbuf, wnd, gc, 0, 0, w, h, 0, 0);
|
||||||
|
XftDrawDestroy(backdraw);
|
||||||
|
XFlush(dpy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_button_press(Display *dpy, GC gc, int mx, int my, XftColor *col, XftFont *f) {
|
||||||
|
SuwaButton *btn = find_button_at(mx, my);
|
||||||
|
if (!btn) return;
|
||||||
|
|
||||||
|
btn->pressed = 1;
|
||||||
|
XftDraw *backdraw = XftDrawCreate(dpy, backbuf,
|
||||||
|
DefaultVisual(dpy, DefaultScreen(dpy)), DefaultColormap(dpy, DefaultScreen(dpy)));
|
||||||
|
if (!backdraw) {
|
||||||
|
fprintf(stderr, "Pixmap向けXftDrawの作成に失敗。\n");
|
||||||
|
XFreePixmap(dpy, backbuf);
|
||||||
|
backbuf = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
drawbuttons(dpy, backbuf, gc, btn, backdraw, col, f);
|
||||||
|
XFlush(dpy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_button_release(Display *dpy, Window wnd, GC gc, int mx, int my, XftColor *col, XftFont *f, XftFont *df, XftFont *pf) {
|
||||||
|
SuwaButton *btn = find_button_at(mx, my);
|
||||||
|
if (!btn) return;
|
||||||
|
|
||||||
|
btn->pressed = 0;
|
||||||
|
|
||||||
|
XftDraw *backdraw = XftDrawCreate(dpy, backbuf,
|
||||||
|
DefaultVisual(dpy, DefaultScreen(dpy)), DefaultColormap(dpy, DefaultScreen(dpy)));
|
||||||
|
if (!backdraw) {
|
||||||
|
fprintf(stderr, "Pixmap向けXftDrawの作成に失敗。\n");
|
||||||
|
XFreePixmap(dpy, backbuf);
|
||||||
|
backbuf = None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
drawbuttons(dpy, backbuf, gc, btn, backdraw, col, f);
|
||||||
|
XftDrawDestroy(backdraw);
|
||||||
|
XFlush(dpy);
|
||||||
|
|
||||||
|
const char *label = btn->label;
|
||||||
|
if (strcmp(label, "C") == 0) {
|
||||||
|
clear_calculator();
|
||||||
|
} else if (strcmp(label, "<") == 0) {
|
||||||
|
curinput[--input_pos] = '\0';
|
||||||
|
strcpy(displaytxt, curinput[0] ? curinput : "0");
|
||||||
|
} else if (strcmp(label, "=") == 0) {
|
||||||
|
double res = evaluate_simple(curinput);
|
||||||
|
if (isnan(res)) {
|
||||||
|
strncpy(displaytxt, "Error", 5);
|
||||||
|
} else {
|
||||||
|
snprintf(displaytxt, sizeof(displaytxt), "%.8g", res);
|
||||||
|
strncpy(curinput, displaytxt, strlen(displaytxt));
|
||||||
|
input_pos = strlen(curinput);
|
||||||
|
}
|
||||||
|
} else if (strlen(label) == 1) {
|
||||||
|
append_to_input(label[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
control_expose(dpy, wnd, gc, &(XEvent){.type = Expose}, col, f, df, pf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handle_key_press(Display *dpy, Window wnd, GC gc, XEvent *event, XftColor *col, XftFont *f, XftFont *df, XftFont *pf) {
|
||||||
|
KeySym keysym;
|
||||||
|
char buf[32];
|
||||||
|
int len;
|
||||||
|
(void)len;
|
||||||
|
|
||||||
|
keysym = XLookupKeysym(&event->xkey, 0);
|
||||||
|
len = XLookupString(&event->xkey, buf, sizeof(buf), &keysym, NULL);
|
||||||
|
|
||||||
|
if (keysym == XK_Q) {
|
||||||
|
isrunning = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysym == XK_B) {
|
||||||
|
puts("標準電卓画面");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysym == XK_S) {
|
||||||
|
puts("関数電卓画面");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysym == XK_P) {
|
||||||
|
puts("プログラマー電卓画面");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysym == XK_H) {
|
||||||
|
puts("履歴画面");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysym == XK_h) {
|
||||||
|
puts("ヘルプ画面");
|
||||||
|
puts("-----");
|
||||||
|
puts("Q = 終了 Exit");
|
||||||
|
puts("X = 1文字の消し Remove 1 character");
|
||||||
|
puts("C = クリ了 Clear");
|
||||||
|
puts("h = ヘルプ画面 Help screen");
|
||||||
|
puts("H = 履歴画面 History screen");
|
||||||
|
puts("B = 標準電卓画面 Basic calculator screen");
|
||||||
|
puts("S = 関数電卓画面 Scientific calculator screen");
|
||||||
|
puts("P = プログラマー電卓画面 Programmer calculator screen");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysym == XK_C) {
|
||||||
|
clear_calculator();
|
||||||
|
goto redraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keysym == XK_Return || keysym == XK_KP_Enter) {
|
||||||
|
double res = evaluate_simple(curinput);
|
||||||
|
if (isnan(res)) {
|
||||||
|
strncpy(displaytxt, "Error", 5);
|
||||||
|
} else {
|
||||||
|
snprintf(displaytxt, sizeof(displaytxt), "%.8g", res);
|
||||||
|
}
|
||||||
|
goto redraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((keysym == XK_BackSpace && input_pos > 0)
|
||||||
|
|| (keysym == XK_Delete && input_pos > 0)
|
||||||
|
|| (keysym == XK_X && input_pos > 0)) {
|
||||||
|
curinput[--input_pos] = '\0';
|
||||||
|
strcpy(displaytxt, curinput[0] ? curinput : "0");
|
||||||
|
goto redraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
char c = 0;
|
||||||
|
if (keysym >= XK_0 && keysym <= XK_9) c = '0' + (keysym - XK_0);
|
||||||
|
else if (keysym >= XK_KP_0 && keysym <= XK_KP_9) c = '0' + (keysym - XK_0);
|
||||||
|
else if (keysym == XK_KP_Add || keysym == XK_plus) c = '+';
|
||||||
|
else if (keysym == XK_KP_Subtract || keysym == XK_minus) c = '-';
|
||||||
|
else if (keysym == XK_KP_Multiply || keysym == XK_asterisk) c = '*';
|
||||||
|
else if (keysym == XK_KP_Divide || keysym == XK_slash) c = '/';
|
||||||
|
else if (keysym == XK_KP_Decimal || keysym == XK_period) c = '.';
|
||||||
|
|
||||||
|
if (c) {
|
||||||
|
append_to_input(c);
|
||||||
|
goto redraw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
redraw:
|
||||||
|
control_expose(dpy, wnd, gc, &(XEvent){.type = Expose}, col, f, df, pf);
|
||||||
|
}
|
||||||
|
|
||||||
|
// void handle_mouse_hover(Display *dpy, Window wnd, GC gc, XEvent *event, XftColor *col, XftFont *f) {
|
||||||
|
// SuwaButton *hover = find_button_at(event->xmotion.x, event->xmotion.y);
|
||||||
|
|
||||||
|
// if (hover != hovered_btn) {
|
||||||
|
// if (hovered_btn) {
|
||||||
|
// hovered_btn->pressed = 0;
|
||||||
|
// redraw_single_button(dpy, wnd, gc, col, f, hovered_btn);
|
||||||
|
// }
|
||||||
|
// hovered_btn = hover;
|
||||||
|
|
||||||
|
// if (hovered_btn) {
|
||||||
|
// redraw_single_button(dpy, wnd, gc, col, f, hovered_btn);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
10
src/control.h
Normal file
10
src/control.h
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xft/Xft.h>
|
||||||
|
|
||||||
|
void control_expose(Display *dpy, Window wnd, GC gc, XEvent *event, XftColor *color, XftFont *f, XftFont *df, XftFont *pf);
|
||||||
|
void handle_button_press(Display *dpy, GC gc, int mx, int my, XftColor *col, XftFont *f);
|
||||||
|
void handle_button_release(Display *dpy, Window wnd, GC gc, int mx, int my, XftColor *col, XftFont *f, XftFont *df, XftFont *pf);
|
||||||
|
void handle_key_press(Display *dpy, Window wnd, GC gc, XEvent *event, XftColor *col, XftFont *f, XftFont *df, XftFont *pf);
|
||||||
|
void handle_mouse_hover(Display *dpy, Window wnd, GC gc, XEvent *event, XftColor *col, XftFont *f);
|
||||||
30
src/display.c
Normal file
30
src/display.c
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#include "display.h"
|
||||||
|
#include "program.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
void drawbuttons(Display *dpy, Drawable target, GC gc, SuwaButton *btn,
|
||||||
|
XftDraw *xftdraw, XftColor *textcolor, XftFont *font) {
|
||||||
|
unsigned long curbg = btn->pressed ? BTSEL : BTCOL;
|
||||||
|
XSetForeground(dpy, gc, curbg);
|
||||||
|
XFillRectangle(dpy, target, gc, btn->x, btn->y, btn->width, btn->height);
|
||||||
|
|
||||||
|
// 文字の中央に
|
||||||
|
if (btn->label && font && xftdraw) {
|
||||||
|
const FcChar8 *str = (const FcChar8 *)btn->label;
|
||||||
|
int len = strlen((const char *)str);
|
||||||
|
|
||||||
|
XGlyphInfo extents;
|
||||||
|
XftTextExtentsUtf8(dpy, font, str, len, &extents);
|
||||||
|
|
||||||
|
int text_w = extents.xOff;
|
||||||
|
int text_h = font->ascent + font->descent;
|
||||||
|
|
||||||
|
int tx = btn->x + (btn->width - text_w) / 2;
|
||||||
|
int ty = btn->y + (btn->height - text_h) / 2 + font->ascent;
|
||||||
|
|
||||||
|
// tx -= extents.x;
|
||||||
|
|
||||||
|
XftDrawStringUtf8(xftdraw, textcolor, font, tx, ty, (FcChar8 *)btn->label, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/display.h
Normal file
6
src/display.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "program.h"
|
||||||
|
|
||||||
|
void drawbuttons(Display *dpy, Drawable target, GC gc, SuwaButton *btn,
|
||||||
|
XftDraw *xftdraw, XftColor *textcolor, XftFont *font);
|
||||||
12
src/program.c
Normal file
12
src/program.c
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#include "program.h"
|
||||||
|
|
||||||
|
int window_width = 382;
|
||||||
|
int window_height = 534;
|
||||||
|
int isrunning = 1;
|
||||||
|
char displayprb[128] = "";
|
||||||
|
char curinput[256] = {0};
|
||||||
|
char displaytxt[64] = "0";
|
||||||
|
int input_pos = 0;
|
||||||
|
Pixmap backbuf = None;
|
||||||
|
SuwaButton *hovered_btn = NULL;
|
||||||
|
SuwaButton *pressed_btn = NULL;
|
||||||
22
src/program.h
Normal file
22
src/program.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <X11/Xlib.h>
|
||||||
|
#include <X11/Xft/Xft.h>
|
||||||
|
#include "ui.h"
|
||||||
|
|
||||||
|
#define FGCOL 0xfcfcfc
|
||||||
|
#define BGCOL 0x232020
|
||||||
|
#define BTSEL 0xb61729
|
||||||
|
#define BTCOL 0xee4030
|
||||||
|
#define BTHVR 0xf35869
|
||||||
|
|
||||||
|
extern int window_width;
|
||||||
|
extern int window_height;
|
||||||
|
extern int isrunning;
|
||||||
|
extern char displayprb[128];
|
||||||
|
extern char curinput[256];
|
||||||
|
extern char displaytxt[64];
|
||||||
|
extern int input_pos;
|
||||||
|
extern Pixmap backbuf;
|
||||||
|
extern SuwaButton *hovered_btn;
|
||||||
|
extern SuwaButton *pressed_btn;
|
||||||
7
src/ui.h
Normal file
7
src/ui.h
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int x, y, width, height;
|
||||||
|
const char *label;
|
||||||
|
int pressed; // 0 = 普通、1 = 押している
|
||||||
|
} SuwaButton;
|
||||||
19
src/utils.c
Normal file
19
src/utils.c
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
|
||||||
|
void cleanup(Display *display, Window window, GC gc,
|
||||||
|
XftColor *color, XftColor *btncolor, XftFont *font,
|
||||||
|
XftFont *disfont, XftFont *prbfont, Colormap colormap, Visual *visual,
|
||||||
|
Pixmap backbuf) {
|
||||||
|
if (btncolor) XftColorFree(display, visual, colormap, btncolor);
|
||||||
|
if (color) XftColorFree(display, visual, colormap, color);
|
||||||
|
if (disfont) XftFontClose(display, disfont);
|
||||||
|
if (prbfont) XftFontClose(display, prbfont);
|
||||||
|
if (font) XftFontClose(display, font);
|
||||||
|
if (gc) XFreeGC(display, gc);
|
||||||
|
if (backbuf) {
|
||||||
|
XFreePixmap(display, backbuf);
|
||||||
|
backbuf = None;
|
||||||
|
}
|
||||||
|
if (window) XDestroyWindow(display, window);
|
||||||
|
if (display) XCloseDisplay(display);
|
||||||
|
}
|
||||||
8
src/utils.h
Normal file
8
src/utils.h
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "program.h"
|
||||||
|
|
||||||
|
void cleanup(Display *display, Window window, GC gc,
|
||||||
|
XftColor *color, XftColor *btncolor, XftFont *font,
|
||||||
|
XftFont *disfont, XftFont *prbfont, Colormap colormap, Visual *visual,
|
||||||
|
Pixmap backbuf);
|
||||||
Reference in New Issue
Block a user