From 7d80006eff8ab92285c8058ce4cfd1398de43f3e Mon Sep 17 00:00:00 2001 From: Ivan Mahonin Date: Mar 08 2025 20:36:16 +0000 Subject: x11 --- diff --git a/simple/x11/lib/build.sh b/simple/x11/lib/build.sh new file mode 100755 index 0000000..76cf900 --- /dev/null +++ b/simple/x11/lib/build.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +set -e + +ROOT="$(cd `dirname "$0"`; pwd)" + +RUN= +DEBUG= +SOUND=1 +SOUNDFILE= + + +while [ "$#" -ne 0 ]; do + if [ "$1" == "-debug" ]; then + DEBUG=1 + elif [ "$1" == "-nosound" ]; then + SOUND= + elif [ "$1" == "-run" ]; then + RUN=1 + else + break + fi + shift +done + + +NAME="$1" +shift +if [ -z "$NAME" ]; then + echo "usage: ./build.sh [-debug] [-nosound] [-run] ... " +fi + + + +FLAGS= +if [ -n "$DEBUG" ]; then + FLAGS="$FLAGS -g -O0" +else + FLAGS="$FLAGS -O3 -DNDEBUG" +fi + +if [ -n "$SOUND" ]; then + FLAGS="$FLAGS -DWITHSOUND -lopenal" +fi + + +echo cc -Wall -I"$ROOT" "$ROOT/"*.c "$@" $FLAGS -lXext -lX11 -lm -o "$NAME" + cc -Wall -I"$ROOT" "$ROOT/"*.c "$@" $FLAGS -lXext -lX11 -lm -o "$NAME" + +if [ -n "$RUN" ]; then + NAMEFULL="$(realpath "$NAME")" + echo "$NAMEFULL" + "$NAMEFULL" +fi + diff --git a/simple/x11/lib/img.c b/simple/x11/lib/img.c new file mode 100644 index 0000000..7db136f --- /dev/null +++ b/simple/x11/lib/img.c @@ -0,0 +1,259 @@ + +#include "img.h" + + +#pragma pack(push,1) +typedef struct { + unsigned char idLength; + unsigned char colormapType; + unsigned char imageType; + unsigned char colormapIndex[2]; + unsigned char colormapLength[2]; + unsigned char colormapSize; + unsigned char xOrigin[2]; + unsigned char yOrigin[2]; + unsigned char width[2]; + unsigned char height[2]; + unsigned char pixelSize; + unsigned char attributes; +} TgaHeader; +#pragma pack(pop) + + +Image imgCreate(int w, int h) { + if (w <= 0 || h <= 0) + return (Image){}; + Color *pixels = malloc(sizeof(Color)*w*h); + if (!pixels) + return LOGERR("imgCreate: cannot alocate memory for pixels (%dx%d)", w, h), (Image){}; + return (Image){ .w = w, .h = h, .data = pixels }; +} + + +void imgFree(Image *img) { + if (!img) return; + LOGDBG("imgFree"); + free(img->data); + *img = (Image){}; +} + + +Image imgCopy(Image img) { + if (!imgValid(img)) return (Image){}; + Image res = imgCreate(img.w, img.h); + if (!imgValid(res)) return (Image){}; + memcpy(res.data, img.data, sizeof(Color)*img.w*img.h); + return res; +} + + +Image imgSub(Image img, int x, int y, int w, int h) { + if (!imgValid(img)) return (Image){}; + if (x < 0) w += x, x = 0; + if (y < 0) h += y, y = 0; + if (x + w > img.w) w = img.w - x; + if (y + h > img.h) h = img.h - y; + if (w <= 0 || h <= 0) return (Image){}; + Image res = imgCreate(w, h); + if (!imgValid(res)) return (Image){}; + for(int i = 0; i < h; ++i) + memcpy(res.data + i*w, img.data + (y+i)*img.w + x, sizeof(Color)*w); + return res; +} + + +void imgClear(Image img) { + if (!imgValid(img)) return; + memset(img.data, 0, sizeof(Color)*img.w*img.h); +} + + +void imgMultA(Image img) { + if (!imgValid(img)) return; + for(Color *p = img.data, *e = p + img.w*img.h; p < e; ++p) { + unsigned int a = p->a; + p->r = (p->r*a + 0xff) >> 8; + p->g = (p->g*a + 0xff) >> 8; + p->b = (p->b*a + 0xff) >> 8; + } +} + + +void imgDivA(Image img) { + if (!imgValid(img)) return; + for(Color *p = img.data, *e = p + img.w*img.h; p < e; ++p) { + unsigned int a = p->a; + if (!a) continue; + p->r = (((unsigned int)p->r << 8) - 1)/a; + p->g = (((unsigned int)p->g << 8) - 1)/a; + p->b = (((unsigned int)p->b << 8) - 1)/a; + } +} + + +int imgVisible(Image img) { + if (!imgValid(img)) return 0; + for(Color *p = img.data, *e = p + img.w*img.h; p < e; ++p) + if (p->a) return 1; + return 0; +} + + +int imgHasPicture(Image img) { + if (!imgValid(img)) return 0; + Color c = {}; + for(Color *p = img.data, *e = p + img.w*img.h; p < e; ++p) + if (p->a) { c = *p; break; } + if (!c.a) return 0; + for(Color *p = img.data, *e = p + img.w*img.h; p < e; ++p) + for(int i = 0; i < 4; ++i) + if (p->c[i] != c.c[i]) return 1; + return 0; +} + + +static void rowResample( + Color *dst, unsigned int dcnt, unsigned int dstep, + Color *src, unsigned int scnt, unsigned int sstep +) { + if (dcnt < scnt) { + // downscale + unsigned int sistep = dcnt << 8, si = sistep, di1 = 0x100, w = 0, sum[4]; + for(Color *e = src + scnt*sstep; src != e; src += sstep, si += sistep) { + unsigned int di = si/scnt; + if (di < di1) { + w += 0xff; + for(int i = 0; i < 4; ++i) + sum[i] += src->c[i]; + } else { + unsigned int k1 = (di - di1)*scnt/dcnt; + unsigned int k0 = 0xff - k1; + w += k0; + for(int i = 0; i < 4; ++i) { + unsigned int c = src->c[i]; + unsigned int cc = (c*k0 + 0xff) >> 8; + dst->c[i] = (sum[i] + cc)*255/w; + sum[i] = (c*k1 + 0xff) >> 8; + } + w = k1; + di1 += 0x100; + dst += dstep; + } + } + } else { + // upscale + if (!--dcnt) dcnt = 1; + if (!--scnt) sstep = 0; + unsigned int di = 0, distep = (scnt << 8) - 1; + for(Color *e = dst + dcnt*dstep + dstep; dst != e; dst += dstep, di += distep) { + unsigned int si = di/dcnt; + unsigned int k1 = si & 0xff; + unsigned int k0 = 0xff - k1; + si >>= 8; + Color *s0 = src + si*sstep; + Color *s1 = s0 + sstep; + for(int i = 0; i < 4; ++i) + dst->c[i] = (s0->c[i]*k0 + s1->c[i]*k1 + 0xff) >> 8; + } + } +} + + +Image imgResampleX(Image img, int w) { + if (!imgValid(img) || w <= 0) + return (Image){}; + if (img.w == w) + return imgCopy(img); + Image res = imgCreate(w, img.h); + for(int i = 0; i < res.h; ++i) + rowResample(res.data + i*w, w, 1, img.data + i*img.w, img.w, 1); + return res; +} + + +Image imgResampleY(Image img, int h) { + if (!imgValid(img) || h <= 0) + return (Image){}; + if (img.h == h) + return imgCopy(img); + Image res = imgCreate(img.w, h); + for(int i = 0; i < res.w; ++i) + rowResample(res.data + i, h, img.w, img.data + i, img.h, img.w); + return res; +} + + +Image imgResample(Image img, int w, int h) { + if (!imgValid(img) || w <= 0 || h <= 0) + return (Image){}; + if (img.w == w && img.h == h) + return imgCopy(img); + if (img.h == h) + return imgResampleX(img, w); + if (img.w == w) + return imgResampleY(img, h); + + //clock_t start = clock(); + Image tmp = imgResampleX(img, w); + Image res = imgResampleY(tmp, h); + imgFree(&tmp); + //LOGDBG("imgResample: %f sec", (clock() - start)/(double)CLOCKS_PER_SEC); + + return res; +} + + +XImage* imgToX(Image *img) { + LOGDBG("imgToX: create XImage"); + if (!img) + return LOGERR("imgToX: bad args"), NULL; + XImage *res = XCreateImage(dpy, visual, 24, ZPixmap, 0, (char*)img->data, img->w, img->h, 32, img->w*sizeof(Color)); + if (!res) + return LOGERR("imgToX: cannot create XImage (%dx%d)", img->w, img->h), imgFree(img), NULL; + *img = (Image){}; + return res; +} + + +Image imgLoadTga(const char *filename) { + LOGDBG("imgLoadTga: open: %s", filename); + FILE *f = fopen(filename, "rb"); + if (!f) + return LOGERR("imgLoadTga: cannot open file for read: %s", filename), (Image){}; + + LOGDBG("imgLoadTga: read header"); + TgaHeader header = {}; + fread(&header, sizeof(header), 1, f); + if ( header.imageType != 2 + || (!header.width[0] && !header.width[1]) + || (!header.height[0] && !header.height[1]) + || header.colormapType + || (header.pixelSize != 24 && header.pixelSize != 32) ) + return LOGERR("imgLoadTga: unsupported format, only uncompressed 24 or 32 bits TGA are supported: %s", filename), fclose(f), (Image){}; + + int w = header.width[1]*256 + header.width[0]; + int h = header.height[1]*256 + header.height[0]; + + LOGDBG("imgLoadTga: create image (%d x %d)", w, h); + Image res = imgCreate(w, h); + if (!imgValid(res)) + return LOGERR("imgLoadTga: cannot create image (%d x %d): %s", w, h, filename), fclose(f), (Image){}; + + LOGDBG("imgLoadTga: read data"); + fseek(f, header.idLength, SEEK_CUR); + Color *row = res.data + w*h; + for(unsigned short r = h; r; --r, row -= w) { + for(Color *c = row - w; c < row; ++c) { + c->r = fgetc(f); + c->g = fgetc(f); + c->b = fgetc(f); + c->a = header.pixelSize == 32 ? fgetc(f) : 255; + } + } + + LOGDBG("imgLoadTga: done"); + fclose(f); + return res; +} + + diff --git a/simple/x11/lib/img.h b/simple/x11/lib/img.h new file mode 100644 index 0000000..59ceb74 --- /dev/null +++ b/simple/x11/lib/img.h @@ -0,0 +1,39 @@ +#ifndef IMG_H +#define IMG_H + + +#include "xmain.h" + + +typedef struct { + union { struct { unsigned char r, g, b, a; }; + struct { unsigned char c[4]; }; }; +} Color; + + +typedef struct { + int w, h; + Color *data; +} Image; + + +static inline int imgValid(Image img) { return img.data && img.w > 0 && img.h > 0; } +Image imgCreate(int w, int h); +void imgFree(Image *img); +Image imgCopy(Image img); +Image imgSub(Image img, int x, int y, int w, int h); + +void imgClear(Image img); +void imgMultA(Image img); +void imgDivA(Image img); +int imgVisible(Image img); +int imgHasPicture(Image img); + +Image imgResampleX(Image img, int w); +Image imgResampleY(Image img, int h); +Image imgResample(Image img, int w, int h); +XImage* imgToX(Image *img); +Image imgLoadTga(const char *filename); + + +#endif \ No newline at end of file diff --git a/simple/x11/lib/snd.c b/simple/x11/lib/snd.c new file mode 100644 index 0000000..9d01ac4 --- /dev/null +++ b/simple/x11/lib/snd.c @@ -0,0 +1,97 @@ +#ifdef WITHSOUND + +#include "snd.h" + + +ALCdevice *adev; +ALCcontext *actx; + + +void sndInit() { + if (adev) return; + adev = alcOpenDevice(NULL); + if (!adev) LOGWRN("sndInit: cannot open device"); + actx = alcCreateContext(adev, NULL); + if (!actx) LOGWRN("sndInit: cannot create context"); + alcMakeContextCurrent(actx); +} + + +void sndDeinit() { + alcMakeContextCurrent(NULL); + if (actx) alcDestroyContext(actx); + if (adev) alcCloseDevice(adev); + actx = NULL; + adev = NULL; +} + + +void sndFree(Sound *snd) { + if (!snd) return; + if (snd->src) alDeleteSources(1, &snd->src); + if (snd->buf) alDeleteBuffers(1, &snd->buf); + *snd = (Sound){}; +} + + +Sound sndGen(int rate, int freq, int samples) { + Sound snd = {}; + if (rate <= 0 || freq <= 0 || samples <= 0) + return snd; + + short *buf = malloc(samples*sizeof(*buf)); + if (!buf) + return LOGERR("sndGen: cannot alocate memory for samples (%d)", samples), (Sound){}; + for(int i = 0; i < samples; ++i) + buf[i] = (int)(32760*sin(2*3.14159*i*freq/rate)); + + alGenBuffers(1, &snd.buf); + alBufferData(snd.buf, AL_FORMAT_MONO16, buf, sizeof(*buf)*samples, rate); + free(buf); + + alGenSources(1, &snd.src); + alSourcei(snd.src, AL_BUFFER, snd.buf); + return snd; +} + + +Sound sndLoadRaw(const char *filename, int rate) { + LOGDBG("sndLoadRaw: open: %s", filename); + FILE *f = fopen(filename, "rb"); + if (!f) + return LOGERR("sndLoadRaw: cannot open file for read: %s", filename), (Sound){}; + + fseek(f, 0, SEEK_END); + int samples = ftell(f)/sizeof(short); + fseek(f, 0, SEEK_SET); + if (samples <= 0) + return LOGERR("sndLoadRaw: file is empty: %s", filename), (Sound){}; + + unsigned short *buf = (unsigned short*)malloc(sizeof(*buf)*samples); + if (!buf) + return LOGERR("sndGen: cannot alocate memory for samples (%d): %s", samples, filename), fclose(f), (Sound){}; + + for(int i = 0; i < samples; ++i) { + unsigned int a = fgetc(f), b = fgetc(f); + buf[i] = a | (b << 8); + } + fclose(f); + + Sound snd = {}; + + alGenBuffers(1, &snd.buf); + alBufferData(snd.buf, AL_FORMAT_MONO16, buf, sizeof(*buf)*samples, rate); + free(buf); + + alGenSources(1, &snd.src); + alSourcei(snd.src, AL_BUFFER, snd.buf); + return snd; +} + + +void sndPlay(Sound snd) { + LOGDBG("sndPlay: %d", snd.src); + alSourcePlay(snd.src); +} + +#endif \ No newline at end of file diff --git a/simple/x11/lib/snd.h b/simple/x11/lib/snd.h new file mode 100644 index 0000000..f7cb034 --- /dev/null +++ b/simple/x11/lib/snd.h @@ -0,0 +1,32 @@ +#ifndef SND_H +#define SND_H + + +#include "xmain.h" + +#ifdef WITHSOUND + +#include +#include + + +typedef struct { + ALuint buf, src; +} Sound; + + +extern ALCdevice *adev; +extern ALCcontext *actx; + + +void sndInit(); +void sndDeinit(); + +static inline int sndValid(Sound snd) { return snd.buf && snd.src; } +void sndFree(Sound *snd); +Sound sndGen(int rate, int freq, int samples); +Sound sndLoadRaw(const char *filename, int rate); +void sndPlay(Sound); + +#endif +#endif diff --git a/simple/x11/lib/xmain.c b/simple/x11/lib/xmain.c new file mode 100644 index 0000000..f57f3b4 --- /dev/null +++ b/simple/x11/lib/xmain.c @@ -0,0 +1,198 @@ + +#include "xmain.h" + + +Display *dpy; +int screen; +Window win; +Drawable drw; +Visual *visual; +GC gc; +int winW, winH; + +static int dblbuf; + +int run; +int exitCode; + + +void stop(int code) { + if (!exitCode) + exitCode = code; + LOGDBG("stop: code %d (%d)", code, exitCode); + run = 0; +} + + +int main() { + LOGDBG("main: open dysplay"); + dpy = XOpenDisplay(NULL); + if (!dpy) + return LOGERR("init: cannot connect to xcb"), 1; + + LOGDBG("main: get screen"); + screen = DefaultScreen(dpy); + + LOGDBG("main: search 24 bit visual"); + XVisualInfo vi = {}; + if (!XMatchVisualInfo(dpy, screen, 24, TrueColor, &vi)) + return LOGERR("main: cannot get 24 bit visual"), XCloseDisplay(dpy), 1; + + LOGDBG("main: query double buffering extension"); + int major, minor; + if (XdbeQueryExtension(dpy, &major, &minor)) { + LOGDBG("main: XdbeGetVisualInfo"); + int numScreens = 1; + Drawable roots[] = { DefaultRootWindow(dpy) }; + XdbeScreenVisualInfo *info = XdbeGetVisualInfo(dpy, roots, &numScreens); + if (info && numScreens > 0 && info->count > 0) { + XVisualInfo visinfo = { + .visualid = info->visinfo[0].visual, + .screen = screen, + .depth = 24 }; + + LOGDBG("main: XGetVisualInfo for double buffering"); + int matches; + XVisualInfo *match = XGetVisualInfo(dpy, VisualIDMask | VisualScreenMask | VisualDepthMask, &visinfo, &matches); + if (match && matches > 0) { + LOGDBG("main: double buffering is supported"); + dblbuf = 1; + vi = *match; + } + } + } + + LOGDBG("main: get root window"); + Window root = RootWindow(dpy, screen); + + LOGDBG("main: create window"); + XSetWindowAttributes attr = {}; + attr.colormap = XCreateColormap(dpy, XDefaultRootWindow(dpy), vi.visual, AllocNone); + drw = win = XCreateWindow( + dpy, root, // display and parent window + 10, 10, 512, 512, 0, // position, size and border width + 24, // depth + CopyFromParent,//InputOutput, // class + vi.visual, // visual + 0,//CWBackPixel | CWColormap | CWBorderPixel, // attributes mask + &attr ); // attributes + + if (dblbuf) { + LOGDBG("main: allocate back buffer"); + drw = XdbeAllocateBackBufferName(dpy, win, XdbeUndefined); + if (!drw) { + LOGWRN("main: cannot allocate backbuffer"); + drw = win; + } + } + + LOGDBG("main: select input"); + XSelectInput( + dpy, win, + StructureNotifyMask + | ExposureMask + | ButtonPressMask + | ButtonReleaseMask ); + + LOGDBG("main: subscribe to windows delete message from window manager"); + Atom aWmDel = XInternAtom(dpy, "WM_DELETE_WINDOW", False); + XSetWMProtocols(dpy, win, &aWmDel, 1); + + LOGDBG("main: map window"); + XMapWindow(dpy, win); + + LOGDBG("main: get window attributes"); + XWindowAttributes wa = {}; + if (!XGetWindowAttributes(dpy, win, &wa)) + return LOGERR("main: cannot get window attributes"), XCloseDisplay(dpy), 1; + visual = wa.visual; + winW = wa.width; + winH = wa.height; + + LOGDBG("main: greate gc"); + gc = XCreateGC(dpy, drw, 0, NULL); + + XFlush(dpy); + + LOGDBG("main: init"); + run = 1; + if (!init()) + stop(1); + + LOGDBG("main: start event loop"); + XEvent event = {}; + int redrawRequested = 0; + while(run) { + XNextEvent(dpy, &event); + int forceDraw = 0; + switch(event.type) { + case Expose: + if (!event.xexpose.count) { + LOGDBG( "main: event: expose: x=%d, y=%d, w=%d, h=%d", + event.xexpose.x, event.xexpose.y, event.xexpose.width, event.xexpose.height ); + redrawRequested = 1; + } + break; + case ConfigureNotify: + if ( event.xconfigure.width + && event.xconfigure.height + && ( winW != event.xconfigure.width + || winH != event.xconfigure.height )) + { + LOGDBG( "main: event: moved: x=%d y=%d w=%d, h=%d", + event.xconfigure.x, event.xconfigure.y, event.xconfigure.width, event.xconfigure.height ); + winW = event.xconfigure.width; + winH = event.xconfigure.height; + resize(); + redrawRequested = 1; + } + break; + case ButtonPress: + if (event.xbutton.button == 1) { + LOGDBG("main: event: mouse down: x=%d, y=%d", event.xbutton.x, event.xbutton.y); + mouseDown(event.xbutton.x, event.xbutton.y); + forceDraw = 1; + } + break; + case ButtonRelease: + if (event.xbutton.button == 1) { + LOGDBG("main: event: mouse up: x=%d, y=%d", event.xbutton.x, event.xbutton.y); + mouseUp(); + forceDraw = 1; + } + break; + case ClientMessage: + if (event.xclient.data.l[0] == aWmDel) { + LOGDBG("main: event: delete window event"); + stop(0); + } + break; + } + + if (forceDraw || (redrawRequested && !XEventsQueued(dpy, QueuedAfterFlush))) { + if (winW > 0 && winH > 0) { + LOGDBG("main: draw"); + draw(); + if (dblbuf) { + XdbeSwapInfo swapInfo = { + .swap_window = win, + .swap_action = XdbeUndefined }; + if (!XdbeSwapBuffers(dpy, &swapInfo, 1)) + LOGWRN("main: cannot swap buffers"); + } + XFlush(dpy); + } + redrawRequested = 0; + } + } + + XFlush(dpy); + + LOGDBG("main: deinit"); + deinit(); + + LOGDBG("main: close display"); + XCloseDisplay(dpy); + return exitCode; +} + diff --git a/simple/x11/lib/xmain.h b/simple/x11/lib/xmain.h new file mode 100644 index 0000000..7e0dc68 --- /dev/null +++ b/simple/x11/lib/xmain.h @@ -0,0 +1,51 @@ +#ifndef XMAIN_H +#define XMAIN_H + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +#define LOG(stream, prefix, suffix, result, ...) \ + ( fputs(prefix, stream), fprintf(stream, __VA_ARGS__), fputs(suffix, stream), fflush(stream), result ) +#ifdef NDEBUG + #define LOGDBG(...) dummy(1) +#else + #define LOGDBG(...) LOG(stdout, "DEBUG: ", "\n", dummy(1), __VA_ARGS__) +#endif +#define LOGINF(...) LOG(stdout, "INFO: ", "\n", dummy(1), __VA_ARGS__) +#define LOGWRN(...) LOG(stderr, "WARNING: ", "\n", dummy(1), __VA_ARGS__) +#define LOGERR(...) LOG(stderr, "ERROR: ", "\n", dummy(0), __VA_ARGS__) +#define DIE(...) LOG(stderr, "ERROR: ", "\n", exit(1), __VA_ARGS__) +static inline int dummy(int i) { return i; } + + +extern Display *dpy; +extern int screen; +extern Window win; +extern Drawable drw; +extern Visual *visual; +extern GC gc; +extern int winW, winH; + + +void stop(int code); + + +int init(); +void deinit(); +void resize(); +void mouseDown(int x, int y); +void mouseUp(); +void draw(); + + +#endif \ No newline at end of file diff --git a/simple/x11/shisen/build.sh b/simple/x11/shisen/build.sh new file mode 100755 index 0000000..52428c7 --- /dev/null +++ b/simple/x11/shisen/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo ../lib/build.sh "$@" shisen shisen.c + ../lib/build.sh "$@" shisen shisen.c \ No newline at end of file diff --git a/simple/x11/shisen/data/back.tga b/simple/x11/shisen/data/back.tga new file mode 100644 index 0000000..1c5abea Binary files /dev/null and b/simple/x11/shisen/data/back.tga differ diff --git a/simple/x11/shisen/data/back1.tga b/simple/x11/shisen/data/back1.tga new file mode 100644 index 0000000..45bfeb4 Binary files /dev/null and b/simple/x11/shisen/data/back1.tga differ diff --git a/simple/x11/shisen/data/back2.tga b/simple/x11/shisen/data/back2.tga new file mode 100644 index 0000000..4331710 Binary files /dev/null and b/simple/x11/shisen/data/back2.tga differ diff --git a/simple/x11/shisen/data/buttons.xcf b/simple/x11/shisen/data/buttons.xcf new file mode 100644 index 0000000..d331477 Binary files /dev/null and b/simple/x11/shisen/data/buttons.xcf differ diff --git a/simple/x11/shisen/data/cells.xcf b/simple/x11/shisen/data/cells.xcf new file mode 100644 index 0000000..b890b53 Binary files /dev/null and b/simple/x11/shisen/data/cells.xcf differ diff --git a/simple/x11/shisen/data/hint.tga b/simple/x11/shisen/data/hint.tga new file mode 100644 index 0000000..73a47e8 Binary files /dev/null and b/simple/x11/shisen/data/hint.tga differ diff --git a/simple/x11/shisen/data/import.sh b/simple/x11/shisen/data/import.sh new file mode 100755 index 0000000..1751d73 --- /dev/null +++ b/simple/x11/shisen/data/import.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +set -e + +cd "$(dirname "$0")" + +function echodo() { + echo "$@" + "$@" +} + + +SRC="../../../../onefile/data/shisen/" +#echodo convert "$SRC/letters-gen.png" ./letters.tga +#echodo convert "$SRC/minas.png" ./minas.tga + +echodo mkdir -p sounds +for file in "$SRC/sounds/"*.ogg; do + f="$(basename "$file")" + echodo ffmpeg -i "$file" -f s16le -acodec pcm_s16le "sounds/${f:0:-4}.raw" +done + +echo "success" + diff --git a/simple/x11/shisen/data/letters.tga b/simple/x11/shisen/data/letters.tga new file mode 100644 index 0000000..787b381 Binary files /dev/null and b/simple/x11/shisen/data/letters.tga differ diff --git a/simple/x11/shisen/data/minas.tga b/simple/x11/shisen/data/minas.tga new file mode 100644 index 0000000..2963dd3 Binary files /dev/null and b/simple/x11/shisen/data/minas.tga differ diff --git a/simple/x11/shisen/data/sounds/01-ayb.raw b/simple/x11/shisen/data/sounds/01-ayb.raw new file mode 100644 index 0000000..b0d57e7 Binary files /dev/null and b/simple/x11/shisen/data/sounds/01-ayb.raw differ diff --git a/simple/x11/shisen/data/sounds/02-ben.raw b/simple/x11/shisen/data/sounds/02-ben.raw new file mode 100644 index 0000000..0501fba Binary files /dev/null and b/simple/x11/shisen/data/sounds/02-ben.raw differ diff --git a/simple/x11/shisen/data/sounds/03-gim.raw b/simple/x11/shisen/data/sounds/03-gim.raw new file mode 100644 index 0000000..be998a9 Binary files /dev/null and b/simple/x11/shisen/data/sounds/03-gim.raw differ diff --git a/simple/x11/shisen/data/sounds/04-da.raw b/simple/x11/shisen/data/sounds/04-da.raw new file mode 100644 index 0000000..c62fd36 Binary files /dev/null and b/simple/x11/shisen/data/sounds/04-da.raw differ diff --git a/simple/x11/shisen/data/sounds/05-yec.raw b/simple/x11/shisen/data/sounds/05-yec.raw new file mode 100644 index 0000000..b5236f5 Binary files /dev/null and b/simple/x11/shisen/data/sounds/05-yec.raw differ diff --git a/simple/x11/shisen/data/sounds/06-za.raw b/simple/x11/shisen/data/sounds/06-za.raw new file mode 100644 index 0000000..cadb9c7 Binary files /dev/null and b/simple/x11/shisen/data/sounds/06-za.raw differ diff --git a/simple/x11/shisen/data/sounds/07-e.raw b/simple/x11/shisen/data/sounds/07-e.raw new file mode 100644 index 0000000..6a50d7e Binary files /dev/null and b/simple/x11/shisen/data/sounds/07-e.raw differ diff --git a/simple/x11/shisen/data/sounds/08-et.raw b/simple/x11/shisen/data/sounds/08-et.raw new file mode 100644 index 0000000..5885856 Binary files /dev/null and b/simple/x11/shisen/data/sounds/08-et.raw differ diff --git a/simple/x11/shisen/data/sounds/09-to.raw b/simple/x11/shisen/data/sounds/09-to.raw new file mode 100644 index 0000000..5366820 Binary files /dev/null and b/simple/x11/shisen/data/sounds/09-to.raw differ diff --git a/simple/x11/shisen/data/sounds/10-ze.raw b/simple/x11/shisen/data/sounds/10-ze.raw new file mode 100644 index 0000000..7201dc7 Binary files /dev/null and b/simple/x11/shisen/data/sounds/10-ze.raw differ diff --git a/simple/x11/shisen/data/sounds/11-ini.raw b/simple/x11/shisen/data/sounds/11-ini.raw new file mode 100644 index 0000000..96f7603 Binary files /dev/null and b/simple/x11/shisen/data/sounds/11-ini.raw differ diff --git a/simple/x11/shisen/data/sounds/12-lyun.raw b/simple/x11/shisen/data/sounds/12-lyun.raw new file mode 100644 index 0000000..3ff7a5b Binary files /dev/null and b/simple/x11/shisen/data/sounds/12-lyun.raw differ diff --git a/simple/x11/shisen/data/sounds/13-xe.raw b/simple/x11/shisen/data/sounds/13-xe.raw new file mode 100644 index 0000000..cf5ec7b Binary files /dev/null and b/simple/x11/shisen/data/sounds/13-xe.raw differ diff --git a/simple/x11/shisen/data/sounds/14-ca.raw b/simple/x11/shisen/data/sounds/14-ca.raw new file mode 100644 index 0000000..27d11c1 Binary files /dev/null and b/simple/x11/shisen/data/sounds/14-ca.raw differ diff --git a/simple/x11/shisen/data/sounds/15-ken.raw b/simple/x11/shisen/data/sounds/15-ken.raw new file mode 100644 index 0000000..efe1a89 Binary files /dev/null and b/simple/x11/shisen/data/sounds/15-ken.raw differ diff --git a/simple/x11/shisen/data/sounds/16-ho.raw b/simple/x11/shisen/data/sounds/16-ho.raw new file mode 100644 index 0000000..449d018 Binary files /dev/null and b/simple/x11/shisen/data/sounds/16-ho.raw differ diff --git a/simple/x11/shisen/data/sounds/17-ja.raw b/simple/x11/shisen/data/sounds/17-ja.raw new file mode 100644 index 0000000..639980d Binary files /dev/null and b/simple/x11/shisen/data/sounds/17-ja.raw differ diff --git a/simple/x11/shisen/data/sounds/18-gat.raw b/simple/x11/shisen/data/sounds/18-gat.raw new file mode 100644 index 0000000..4526fa3 Binary files /dev/null and b/simple/x11/shisen/data/sounds/18-gat.raw differ diff --git a/simple/x11/shisen/data/sounds/19-ce.raw b/simple/x11/shisen/data/sounds/19-ce.raw new file mode 100644 index 0000000..b4c67da Binary files /dev/null and b/simple/x11/shisen/data/sounds/19-ce.raw differ diff --git a/simple/x11/shisen/data/sounds/20-men.raw b/simple/x11/shisen/data/sounds/20-men.raw new file mode 100644 index 0000000..e77bc53 Binary files /dev/null and b/simple/x11/shisen/data/sounds/20-men.raw differ diff --git a/simple/x11/shisen/data/sounds/21-hi.raw b/simple/x11/shisen/data/sounds/21-hi.raw new file mode 100644 index 0000000..d1eee74 Binary files /dev/null and b/simple/x11/shisen/data/sounds/21-hi.raw differ diff --git a/simple/x11/shisen/data/sounds/22-nu.raw b/simple/x11/shisen/data/sounds/22-nu.raw new file mode 100644 index 0000000..eddd784 Binary files /dev/null and b/simple/x11/shisen/data/sounds/22-nu.raw differ diff --git a/simple/x11/shisen/data/sounds/23-sa.raw b/simple/x11/shisen/data/sounds/23-sa.raw new file mode 100644 index 0000000..c8f78cf Binary files /dev/null and b/simple/x11/shisen/data/sounds/23-sa.raw differ diff --git a/simple/x11/shisen/data/sounds/24-vo.raw b/simple/x11/shisen/data/sounds/24-vo.raw new file mode 100644 index 0000000..dabc5c3 Binary files /dev/null and b/simple/x11/shisen/data/sounds/24-vo.raw differ diff --git a/simple/x11/shisen/data/sounds/25-cha.raw b/simple/x11/shisen/data/sounds/25-cha.raw new file mode 100644 index 0000000..eb6ae26 Binary files /dev/null and b/simple/x11/shisen/data/sounds/25-cha.raw differ diff --git a/simple/x11/shisen/data/sounds/26-pe.raw b/simple/x11/shisen/data/sounds/26-pe.raw new file mode 100644 index 0000000..15a7669 Binary files /dev/null and b/simple/x11/shisen/data/sounds/26-pe.raw differ diff --git a/simple/x11/shisen/data/sounds/27-je.raw b/simple/x11/shisen/data/sounds/27-je.raw new file mode 100644 index 0000000..8808de9 Binary files /dev/null and b/simple/x11/shisen/data/sounds/27-je.raw differ diff --git a/simple/x11/shisen/data/sounds/28-ra.raw b/simple/x11/shisen/data/sounds/28-ra.raw new file mode 100644 index 0000000..ed4c50b Binary files /dev/null and b/simple/x11/shisen/data/sounds/28-ra.raw differ diff --git a/simple/x11/shisen/data/sounds/29-se.raw b/simple/x11/shisen/data/sounds/29-se.raw new file mode 100644 index 0000000..d89d273 Binary files /dev/null and b/simple/x11/shisen/data/sounds/29-se.raw differ diff --git a/simple/x11/shisen/data/sounds/30-vew.raw b/simple/x11/shisen/data/sounds/30-vew.raw new file mode 100644 index 0000000..4fdaa39 Binary files /dev/null and b/simple/x11/shisen/data/sounds/30-vew.raw differ diff --git a/simple/x11/shisen/data/sounds/31-tiwn.raw b/simple/x11/shisen/data/sounds/31-tiwn.raw new file mode 100644 index 0000000..677ee2f Binary files /dev/null and b/simple/x11/shisen/data/sounds/31-tiwn.raw differ diff --git a/simple/x11/shisen/data/sounds/32-re.raw b/simple/x11/shisen/data/sounds/32-re.raw new file mode 100644 index 0000000..22ce232 Binary files /dev/null and b/simple/x11/shisen/data/sounds/32-re.raw differ diff --git a/simple/x11/shisen/data/sounds/33-co.raw b/simple/x11/shisen/data/sounds/33-co.raw new file mode 100644 index 0000000..de5d85a Binary files /dev/null and b/simple/x11/shisen/data/sounds/33-co.raw differ diff --git a/simple/x11/shisen/data/sounds/34-u.raw b/simple/x11/shisen/data/sounds/34-u.raw new file mode 100644 index 0000000..32bc292 Binary files /dev/null and b/simple/x11/shisen/data/sounds/34-u.raw differ diff --git a/simple/x11/shisen/data/sounds/35-pyur.raw b/simple/x11/shisen/data/sounds/35-pyur.raw new file mode 100644 index 0000000..2b4dfbc Binary files /dev/null and b/simple/x11/shisen/data/sounds/35-pyur.raw differ diff --git a/simple/x11/shisen/data/sounds/36-ke.raw b/simple/x11/shisen/data/sounds/36-ke.raw new file mode 100644 index 0000000..a063b27 Binary files /dev/null and b/simple/x11/shisen/data/sounds/36-ke.raw differ diff --git a/simple/x11/shisen/data/sounds/37-o.raw b/simple/x11/shisen/data/sounds/37-o.raw new file mode 100644 index 0000000..ddb99c9 Binary files /dev/null and b/simple/x11/shisen/data/sounds/37-o.raw differ diff --git a/simple/x11/shisen/data/sounds/38-fe.raw b/simple/x11/shisen/data/sounds/38-fe.raw new file mode 100644 index 0000000..042a621 Binary files /dev/null and b/simple/x11/shisen/data/sounds/38-fe.raw differ diff --git a/simple/x11/shisen/data/sounds/39-yew.raw b/simple/x11/shisen/data/sounds/39-yew.raw new file mode 100644 index 0000000..fb240a1 Binary files /dev/null and b/simple/x11/shisen/data/sounds/39-yew.raw differ diff --git a/simple/x11/shisen/data/start.tga b/simple/x11/shisen/data/start.tga new file mode 100644 index 0000000..99b1be1 Binary files /dev/null and b/simple/x11/shisen/data/start.tga differ diff --git a/simple/x11/shisen/shisen.c b/simple/x11/shisen/shisen.c new file mode 100644 index 0000000..b27a3d5 --- /dev/null +++ b/simple/x11/shisen/shisen.c @@ -0,0 +1,550 @@ + +#include +#include + +#include +#include + + +#define COLS 16 +#define ROWS 9 +#define ACTIVELETTERS 20 +#define MAXLETTERS 256 +#define MAXSOUNDS 16 +#define RMTIME 1.0 +#define EXTIME 2.0 +#define SNDRATE 44100 + +#define COUNT (ROWS*COLS) + + +typedef struct { + Image src; + XImage *x; +} ImageEx; + +typedef struct { + ImageEx glyphs[4]; + int glyphsCount; + Sound sounds[MAXSOUNDS]; + int soundsCount; + ImageEx *g[4]; +} Letter; + +typedef struct { + int r, c; +} Pos; + +typedef struct { + int len; + Pos p[4]; +} Track; + +typedef struct { + Letter *l; + ImageEx *g; +} Cell; + + +Letter menuLetter; +Letter letters[MAXLETTERS]; +int lettersCount; +int cellWidth = 1; +int cellHeight = 1; + +Cell board[ROWS][COLS]; +Pos hover = { -1, -1 }; +Pos selected = { -1, -1 }; +Track tracks[COUNT]; +int tracksCount; +int glyphsMask = 0; +int mode; +int showhint; +int back; +int ox, oy, cw, ch; + +ImageEx bgtex; +ImageEx starttex; +ImageEx hinttex; +ImageEx backtex[3]; + + +void imgExFree(ImageEx *img) { + if (img->x) XDestroyImage(img->x); + imgFree(&img->src); + *img = (ImageEx){}; +} + + +void imgExDraw(ImageEx *img, int x, int y, int w, int h) { + if (!imgValid(img->src) || w <= 0 || h <= 0) return; + if (img->x && (img->x->width != w || img->x->height != h)) + { XDestroyImage(img->x); img->x = NULL; } + if (!img->x) { + Image tmp = imgResample(img->src, w, h); + imgDivA(tmp); + img->x = imgToX(&tmp); + } + if (img->x) + XPutImage(dpy, drw, gc, img->x, 0, 0, x, y, img->x->width, img->x->height); +} + + +int imgExLoadTga(ImageEx *img, const char *filename) { + imgExFree(img); + img->src = imgLoadTga(filename); + imgMultA(img->src); + return imgValid(img->src); +} + + +void clearBoard() { + mode = 0; + back = 0; + memset(tracks, 0, sizeof(tracks)); + memset(board, 0, sizeof(board)); + tracksCount = 0; + selected = (Pos){ -1, -1 }; +} + + +void clearSounds() { + for(int i = 0; i < lettersCount; ++i) + for(int j = 0; j < letters[i].soundsCount; ++i) + sndFree(&letters[i].sounds[j]); +} + + +void clearLetters() { + clearBoard(); + clearSounds(); + for(int i = 0; i < lettersCount; ++i) + for(int j = 0; j < letters[i].glyphsCount; ++j) + imgExFree(&letters[i].glyphs[j]); + memset(letters, 0, sizeof(letters)); + memset(&menuLetter, 0, sizeof(menuLetter)); + lettersCount = 0; + cellWidth = cellHeight = 1; +} + + +int loadLetters(const char *filename, int rows, int cols) { + clearLetters(); + + Image img = imgLoadTga(filename); + if (!imgValid(img)) + return 0; + int ww = img.w/cols/2; + int hh = img.h/rows/2; + if (!ww || !hh) + return LOGERR("loadLetters: image too small (%dx%d): %s", img.w, img.h, filename), imgFree(&img), 0; + + imgMultA(img); + Letter *letter = letters; + for(int r = 0; r < rows; ++r) + for(int c = 0; c < cols; ++c) + { + for(int rr = 0; rr < 2; ++rr) + for(int cc = 0; cc < 2; ++cc) { + Image sub = imgSub(img, (c*2 + cc)*ww, (r*2 + rr)*hh, ww, hh); + if (!imgHasPicture(sub)) + { imgFree(&sub); continue; } + letter->glyphs[ letter->glyphsCount++ ].src = sub; + } + if (letter->glyphsCount > menuLetter.glyphsCount) menuLetter = *letter; + if (letter->glyphsCount > 0) ++letter; + } + lettersCount = letter - letters; + cellWidth = ww; + cellHeight = hh; + assert(menuLetter.glyphsCount == 4); + + imgFree(&img); + return 1; +} + + +void loadSounds(const char *path) { + DIR *dir = opendir(path); + if (!dir) return; + size_t pl = strlen(path); + + char buf[1024]; + while(1) { + struct dirent *e = readdir(dir); + if (!e) break; + size_t nl = strlen(e->d_name); + if (nl < 4 || pl + nl + 1 >= sizeof(buf)) continue; + + const char *ex = e->d_name + nl - 4; + if ( ex[0] != '.' + || tolower(ex[1]) != 'r' + || tolower(ex[2]) != 'a' + || tolower(ex[3]) != 'w' ) continue; + + int index = atoi(e->d_name); + if (index <= 0 || index > lettersCount) continue; + + Letter *l = &letters[index-1]; + if (l->soundsCount >= MAXSOUNDS) continue; + + sprintf(buf, "%s/%s", path, e->d_name); + l->sounds[l->soundsCount] = sndLoadRaw(buf, SNDRATE); + //l->sounds[l->soundsCount] = sndGen(SNDRATE, 100, SNDRATE); + if (sndValid(l->sounds[l->soundsCount])) ++l->soundsCount; + } + closedir(dir); +} + + +void shuffleLetters(int glyps) { + for(int i = 0; i < lettersCount; ++i) { + int j = rand()%(lettersCount - i) + i; + if (j != i) { Letter l = letters[i]; letters[i] = letters[j]; letters[j] = l; } + Letter *l = &letters[i]; + assert(l->glyphsCount > 0); + + int gc = 0; + ImageEx *g[4] = {}; + for(int j = 0; j < l->glyphsCount; ++j) + if (glyps & (1 << j)) g[gc++] = &l->glyphs[j]; + if (!gc) g[gc++] = &l->glyphs[0]; + + for(int j = 0; j < gc; ++j) { + int k = rand()%(gc - j) + j; + if (k != j) { ImageEx *a = g[j]; g[j] = g[k]; g[k] = a; } + } + + for(int j = 0; j < 4; ++j) + l->g[j] = g[j%gc]; + + for(int j = 0; j < 4; ++j) { + int k = rand()%(4 - j) + j; + if (k != j) { ImageEx *g = l->g[j]; l->g[j] = l->g[k]; l->g[k] = g; } + assert(l->g[j]); + } + } +} + + +void simpleBoard() { + clearBoard(); + for(int i = 0; i < COUNT; ++i) { + Letter *l = &letters[i%lettersCount]; + board[i/COLS][i%COLS].l = l; + board[i/COLS][i%COLS].g = &l->glyphs[1]; + } + mode = 1; +} + + +void generateBoard(int glyphsMask, int maxLetters) { + if (maxLetters < 1) maxLetters = 1; + + clearBoard(); + shuffleLetters(glyphsMask); + + int indices[COUNT]; + for(int i = 0; i < COUNT; ++i) indices[i] = i; + + int lc = lettersCount < maxLetters ? lettersCount : maxLetters; + for(int i = 0; i < COUNT; ++i) { + int j = rand()%(COUNT - i) + i; + int id = indices[j]; indices[j] = indices[i]; indices[i] = id; + Letter *l = &letters[ (id/4)%lc ]; + board[i/COLS][i%COLS].l = l; + board[i/COLS][i%COLS].g = l->g[id%4]; + } + + mode = 1; +} + + +Letter* get(Pos p) + { return p.r >= 0 && p.r < ROWS && p.c >= 0 && p.c < COLS ? board[p.r][p.c].l : NULL; } + + +int findSubTrack1(Track *t, Pos b) { + Pos p = t->p[t->len - 1]; + //printf(" -- -- ft1: %d %d -> %d %d\n", p.r, p.c, b.r, b.c); + if (p.c == b.c && p.r < b.r) { for(++p.r; p.r != b.r; ++p.r) if (get(p)) return 0; } else + if (p.c == b.c && p.r > b.r) { for(--p.r; p.r != b.r; --p.r) if (get(p)) return 0; } else + if (p.r == b.r && p.c < b.c) { for(++p.c; p.c != b.c; ++p.c) if (get(p)) return 0; } else + if (p.r == b.r && p.c > b.c) { for(--p.c; p.c != b.c; --p.c) if (get(p)) return 0; } else return 0; + t->p[t->len++] = p; + return 1; +} + + +int findSubTrack2(Track *t, Pos b, int horz) { + Pos *p = &t->p[t->len]; + *p = t->p[t->len - 1]; + + //printf(" -- ft2: %d %d -> %d %d %s\n", p->r, p->c, b.r, b.c, horz ? "horz" : "vert"); + if (p->c == b.c || p->r == b.r) return findSubTrack1(t, b); + + + ++t->len; + if (horz) { + if (p->c < b.c) { for(++p->c; p->c != b.c; ++p->c) if (get(*p)) break; } + else { for(--p->c; p->c != b.c; --p->c) if (get(*p)) break; } + } else { + if (p->r < b.r) { for(++p->r; p->r != b.r; ++p->r) if (get(*p)) break; } + else { for(--p->r; p->r != b.r; --p->r) if (get(*p)) break; } + } + if (!get(*p) && findSubTrack1(t, b)) return 1; + --t->len; + + return 0; +} + + +int findSubTrack3(Track *t, Pos b) { + Pos p0 = t->p[t->len - 1]; + Pos *p = &t->p[t->len]; + if (p->c == b.c && p->r == b.r) return 0; + + //printf("ft3: %d %d -> %d %d\n", p0.r, p0.c, b.r, b.c); + if (findSubTrack2(t, b, 0)) return 1; + + ++t->len; + + *p = p0; + for(++p->c; p->c <= COLS; ++p->c) + if (get(*p)) break; else + if (findSubTrack2(t, b, 0)) return 1; + + *p = p0; + for(--p->c; p->c >= -1; --p->c) + if (get(*p)) break; else + if (findSubTrack2(t, b, 0)) return 1; + + *p = p0; + for(++p->r; p->r <= ROWS; ++p->r) + if (get(*p)) break; else + if (findSubTrack2(t, b, 1)) return 1; + + *p = p0; + for(--p->r; p->r >= -1; --p->r) + if (get(*p)) break; else + if (findSubTrack2(t, b, 1)) return 1; + + --t->len; + return 0; +} + + +Track findTrack(Pos a, Pos b) { + Track t = {}; + t.p[t.len++] = a; + Letter *la = get(a); + Letter *lb = get(b); + return la && la == lb && (a.c != b.c || a.r != b.r) && findSubTrack3(&t, b) ? t : (Track){}; +} + + +void hint() { + if (tracksCount) return; + + for(int i = 0; i < COUNT; ++i) { + Pos a = { i/COLS, i%COLS }; + for(int j = 0; j < COUNT; ++j) { + Pos b = { j/COLS, j%COLS }; + Track t = findTrack(a, b); + if (t.len) + tracks[tracksCount++] = t; + } + } + + LOGDBG("hint: tracks count: %d", tracksCount); + if (!tracksCount) return; // beep + + int i = rand()%tracksCount; + if (i) tracks[0] = tracks[i]; + tracksCount = 1; +} + + +int init() { + sndInit(); + srand(time(NULL)); + imgExLoadTga(&bgtex, "data/minas.tga"); + imgExLoadTga(&starttex, "data/start.tga"); + imgExLoadTga(&hinttex, "data/hint.tga"); + imgExLoadTga(&backtex[0], "data/back.tga"); + imgExLoadTga(&backtex[1], "data/back1.tga"); + imgExLoadTga(&backtex[2], "data/back2.tga"); + if (!loadLetters("data/letters.tga", 5, 8)) + return LOGERR("init: cannot load letters"), 0; + loadSounds("data/sounds"); + //simpleBoard(); + return 1; +} + + +void deinit() { + clearLetters(); + imgExFree(&bgtex); + imgExFree(&starttex); + imgExFree(&hinttex); + imgExFree(&backtex[0]); + imgExFree(&backtex[1]); + imgExFree(&backtex[2]); + sndDeinit(); +} + + +void resize() { } + + +void mouseDown(int x, int y) { + int mc = (x - ox)/cw; + int mr = (y - oy)/ch; + if (x < ox) --mc; + if (y < oy) --mr; + + if (mode == 0) { + if (!mr && mc >= 0 && mc < 4) { + glyphsMask ^= 1 << mc; + } else + if (mr == 1 && mc >= 1 && mc <= 2) { + generateBoard(glyphsMask, ACTIVELETTERS); + } + } else { + if (mc < 0 && mr < 0) { + if (++back >= 3) clearBoard(); + return; + } else back = 0; + + if (mc >= COLS && mr >= ROWS) { + if (get(selected)) showhint = 1; + else hint(); + } else + if (mc >= 0 && mr >= 0 && mc < COLS && mr < COLS) { + hover.r = mr; + hover.c = mc; + Letter *l = get(hover); + if (l && l->soundsCount > 0) + sndPlay(l->sounds[rand() % l->soundsCount]); + if (!l || (mr == selected.r && mc == selected.c)) { + selected.r = selected.c = hover.r = hover.c = -1; + } else + if (l == get(selected)) { + Track t = findTrack(selected, hover); + if (t.len) { + Cell *ca = &board[selected.r][selected.c]; + Cell *cb = &board[hover.r][hover.c]; + ca->l = cb->l = NULL; + tracks[tracksCount++] = t; + } else { + selected = hover; + } + } else { + selected = hover; + } + } else { + selected.r = selected.c = hover.r = hover.c = -1; + } + } +} + + +void mouseUp() { + if (mode) { + showhint = 0; + tracksCount = 0; + for(int r = 0; r < ROWS; ++r) + for(int c = 0; c < COLS; ++c) + if (!board[r][c].l) board[r][c].g = NULL; + } +} + + +void draw() { + if (imgValid(bgtex.src)) { + if (winW*bgtex.src.h > winH*bgtex.src.w) { + int h = bgtex.src.h*winW/bgtex.src.w; + imgExDraw(&bgtex, 0, (winH - h)/2, winW, h); + } else { + int w = bgtex.src.w*winH/bgtex.src.h; + imgExDraw(&bgtex, (winW - w)/2, 0, w, winH); + } + } + + if (mode == 0) { + if (winW*cellHeight*3 < winH*cellWidth*6) { + cw = winW/6; + ch = cellHeight*cw/cellWidth; + } else { + ch = winH/3; + cw = cellWidth*ch/cellHeight; + } + ox = (winW - cw*4)/2; + oy = (winH - ch)/2; + + int lw = cw/6; + if (lw < 1) lw = 1; + XSetForeground(dpy, gc, 0x0000ff); + XSetLineAttributes(dpy, gc, lw, LineSolid, CapRound, JoinRound); + + for(int i = 0; i < 4; ++i) + imgExDraw(&menuLetter.glyphs[i], ox + i*cw, oy, cw, ch); + if (glyphsMask) + imgExDraw(&starttex, ox + cw, oy + ch, 2*cw, ch); + for(int i = 0; i < 4; ++i) + if (glyphsMask & (1 << i)) + XDrawRectangle(dpy, drw, gc, ox + i*cw, oy, cw, ch); + } else { + if (winW*cellHeight*(ROWS + 2) < winH*cellWidth*(COLS + 4)) { + cw = winW/(COLS + 4); + ch = cellHeight*cw/cellWidth; + } else { + ch = winH/(ROWS + 2); + cw = cellWidth*ch/cellHeight; + } + ox = (winW - cw*COLS)/2; + oy = (winH - ch*ROWS)/2; + + int lw = cw/6; + if (lw < 1) lw = 1; + XSetForeground(dpy, gc, 0x0000ff); + XSetLineAttributes(dpy, gc, lw, LineSolid, CapRound, JoinRound); + + // draw buttons + imgExDraw(&backtex[back], ox - 2*cw, oy - ch, 2*cw, ch); + imgExDraw(&hinttex, ox + COLS*cw, oy + ROWS*ch, 2*cw, ch); + + // draw cells + Letter *hl = showhint ? get(selected) : NULL; + for(int r = 0; r < ROWS; ++r) + for(int c = 0; c < COLS; ++c) { + Cell *cell = &board[r][c]; + if (!cell->g) continue; + imgExDraw(cell->g, ox + c*cw, oy + r*ch, cw, ch); + } + + // draw selection + for(int r = 0; r < ROWS; ++r) + for(int c = 0; c < COLS; ++c) { + Cell *cell = &board[r][c]; + if (!cell->g) continue; + if ( (r == hover.r && c == hover.c) + || (r == selected.r && c == selected.c) + || (hl && hl == cell->l) ) + XDrawRectangle(dpy, drw, gc, ox + c*cw, oy + r*ch, cw, ch); + } + + // draw tracks + XSetForeground(dpy, gc, 0xff0000); + for(int i = 0; i < tracksCount; ++i) { + Track *t = &tracks[i]; + XPoint p[4]; + for(int j = 0; j < t->len; ++j) { + p[j].x = ox + t->p[j].c*cw + cw/2; + p[j].y = oy + t->p[j].r*ch + ch/2; + } + XDrawLines(dpy, drw, gc, p, t->len, CoordModeOrigin); + } + } +} + diff --git a/simple/x11/test/app.c b/simple/x11/test/app.c new file mode 100644 index 0000000..725696f --- /dev/null +++ b/simple/x11/test/app.c @@ -0,0 +1,35 @@ + +#include "img.h" + + +Image img; +XImage *imgr; + + +int init() { + img = imgLoadTga("text.tga"); + return imgValid(img); +} + +void deinit() { + imgFree(&img); +} + +void resize() { + if (imgr) XDestroyImage(imgr); + imgr = NULL; +} + + +void mouseDown(int x, int y) {} +void mouseUp() {} + +void draw() { + if (!imgr) { + Image tmp = imgResample(img, winW, winH); + imgr = imgToX(&tmp); + } + if (imgr) + XPutImage(dpy, drw, gc, imgr, 0, 0, 0, 0, imgr->width, imgr->height); +} + diff --git a/simple/x11/test/build.sh b/simple/x11/test/build.sh new file mode 100755 index 0000000..b174cec --- /dev/null +++ b/simple/x11/test/build.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +echo ../lib/build.sh "$@" test app.c + ../lib/build.sh "$@" test app.c \ No newline at end of file diff --git a/simple/x11/test/text.tga b/simple/x11/test/text.tga new file mode 100644 index 0000000..ab12a62 Binary files /dev/null and b/simple/x11/test/text.tga differ