1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://www.wtfpl.net/txt/copying/ for more details.
16 #include "videolib/videolib.h"
17 #include "gestureng/gestureng.h"
23 static gengesture_t stk
;
24 static gengtemplate_t stkpat
;
25 static int stkpat_inited
= 0;
26 static genglib_t stklib
;
27 static char *message
= NULL
;
28 static SDL_TimerID tid_msghide
= 0;
29 static SDL_TimerID tid_frame
= 0;
30 static int frame_changed
= 0;
33 ////////////////////////////////////////////////////////////////////////////////
40 ////////////////////////////////////////////////////////////////////////////////
41 static void draw_stroke (const gengesture_t
*stk
) {
43 Uint32 col
= rgb2col(255, 255, 0);
44 for (int f
= 1; f
< stk
->count
-1; ++f
) {
45 int x0
= geng_ptx(stk
, f
-1);
46 int y0
= geng_pty(stk
, f
-1);
47 int x1
= geng_ptx(stk
, f
);
48 int y1
= geng_pty(stk
, f
);
49 draw_line(x0
, y0
, x1
, y1
, col
);
55 static void draw_template (const gengtemplate_t stk
) {
56 for (int f
= 1; f
< GENG_PTS_PER_STK
; ++f
) {
57 int g
= 255*f
/(GENG_PTS_PER_STK
-1), b
= 255-(255*f
/(GENG_PTS_PER_STK
-1));
58 Uint32 col
= rgb2col(0, g
, b
);
59 double x0
= stk
[(f
-1)*2+0];
60 double y0
= stk
[(f
-1)*2+1];
61 double x1
= stk
[f
*2+0];
62 double y1
= stk
[f
*2+1];
67 draw_line(x0
, y0
, x1
, y1
, col
);
72 ////////////////////////////////////////////////////////////////////////////////
73 static int stklist_curptr
= -1;
74 static char **stknames
= NULL
;
75 static int stknames_count
= 0;
76 static int stknames_maxlen
= 0;
77 static const char *curstkname
= NULL
;
80 static void stroke_list_clear (void) {
81 for (int f
= stknames_count
-1; f
>= 0; --f
) free(stknames
[f
]);
90 static void stroke_list_rebuild (void) {
91 int cmp (const void *ps0
, const void *ps1
) {
92 const char **s0
= (const char **)ps0
;
93 const char **s1
= (const char **)ps1
;
94 return strcmp(*s0
, *s1
);
98 for (int f
= 0; f
< stklib
.count
; ++f
) {
100 for (int c
= 0; c
< stknames_count
&& !found
; ++c
) if (strcmp(stklib
.names
[f
], stknames
[c
]) == 0) found
= 1;
102 stknames
= realloc(stknames
, sizeof(stknames
[0])*(stknames_count
+1));
103 stknames
[stknames_count
++] = strdup(stklib
.names
[f
]);
104 //fprintf(stderr, "[%s]\n", stknames[stknames_count-1]);
105 if (strlen(stklib
.names
[f
]) > stknames_maxlen
) stknames_maxlen
= strlen(stklib
.names
[f
]);
108 qsort(stknames
, stknames_count
, sizeof(stknames
[0]), cmp
);
109 stknames_maxlen
= stknames_maxlen
*12+8;
113 static int stroke_list_curptr (int x
, int y
) {
114 if (x
>= 4 && y
>= 4 && x
< stknames_maxlen
-8 && y
< stknames_count
*16+4) return (y
-4)/16;
119 static void draw_stroke_list (int curptr
) {
120 if (stknames_count
> 0) {
121 fill_rect(0, 0, stknames_maxlen
, stknames_count
*16+8, rgb2col(0, 0, 127));
123 for (int f
= 0; f
< stknames_count
; ++f
) {
125 if (curstkname
!= NULL
&& strcmp(stknames
[f
], curstkname
) == 0) {
126 col
= rgb2col(255, 255, 255);
127 bkcol
= rgb2col(0, 0, 255);
129 col
= rgb2col(255, 127, 0);
130 bkcol
= rgb2col(0, 0, 127);
132 if (curptr
== f
) bkcol
= rgb2col(0, 127, 0);
133 fill_rect(0, f
*16+4, stknames_maxlen
, 16, bkcol
);
134 draw_str6(4, f
*16+4, stknames
[f
], col
, TRANSPARENT_COLOR
);
136 if (stknames_count
> 0) {
137 draw_rect(0, 0, stknames_maxlen
, stknames_count
*16+8, rgb2col(255, 255, 255));
138 draw_rect(1, 1, stknames_maxlen
-2, stknames_count
*16+6, rgb2col(0, 0, 0));
143 ////////////////////////////////////////////////////////////////////////////////
144 static int show_help
= 0;
145 static const char *help_text
[] = {
146 "\x1fProtractor demo actions",
147 "\x1f-----------------------",
149 " \2F1\1: toggle help",
150 " \2F2\1: save library to '\4strokes.dat\1'",
151 " \2F3\1: replace library with '\4strokes.dat\1'",
152 " \2F8\1: clear library",
154 " \2DEL\1: delete selected stroke",
157 " \2LMB\1: select name or start drawing",
158 " \2RMB\1: register current stroke as template for selected name",
159 " \2MMB\1: register current stroke as template for new name",
163 static int stlen (const char *str
) {
165 for (; *str
; ++str
) if ((unsigned char)(str
[0]) >= 32) ++len
;
170 static void stdraw (int x
, int y
, const char *str
) {
171 Uint32 fg
= rgb2col(255, 255, 255);
172 if (str
[0] == '\x1f') {
173 x
= (SCR_WIDTH
-stlen(str
)*12)/2;
176 for (; *str
; ++str
) {
177 unsigned char ch
= (unsigned char)(str
[0]);
180 case 1: fg
= rgb2col(255, 255, 255); break;
181 case 2: fg
= rgb2col(0, 255, 0); break;
182 case 3: fg
= rgb2col(255, 255, 0); break;
183 case 4: fg
= rgb2col(255, 127, 0); break;
186 draw_char8(x
, y
, ch
, fg
, TRANSPARENT_COLOR
);
193 static void toggle_help (void) {
194 show_help
= !show_help
;
198 static void draw_help (void) {
201 int wdt
= 0, hgt
= sizeof(help_text
)/sizeof(help_text
[0])*16+8;
202 Uint32 fg
= rgb2col(255, 255, 255);
203 Uint32 bg
= rgb2col(25, 69, 247);
204 for (size_t f
= 0; f
< sizeof(help_text
)/sizeof(help_text
[0]); ++f
) {
205 int l
= stlen(help_text
[f
]);
206 if (l
> wdt
) wdt
= l
;
209 x
= (SCR_WIDTH
-wdt
)/2;
210 y
= (SCR_HEIGHT
-hgt
)/2;
211 fill_rect(x
, y
, wdt
, hgt
, fg
);
212 fill_rect(x
+1, y
+1, wdt
-2, hgt
-2, rgb2col(0, 0, 0));
213 fill_rect(x
+2, y
+2, wdt
-4, hgt
-4, bg
);
216 for (size_t f
= 0; f
< sizeof(help_text
)/sizeof(help_text
[0]); ++f
, y
+= 16) stdraw(x
, y
, help_text
[f
]);
221 ////////////////////////////////////////////////////////////////////////////////
222 static void rebuild_screen (void) {
223 fill_rect(0, 0, SCR_WIDTH
, SCR_HEIGHT
, rgb2col(0, 0, 0));
225 if (stkpat_inited
) draw_template(stkpat
);
226 draw_stroke_list(stklist_curptr
);
227 if (message
!= NULL
) {
228 int w
= strlen(message
)*16;
230 int x
= (SCR_WIDTH
-w
)/2;
231 int y
= SCR_HEIGHT
-h
-2;
232 Uint32 fg
= rgb2col(255, 255, 255);
233 Uint32 bg
= rgb2col(25, 69, 247);
234 Uint32 blk
= rgb2col(0, 0, 0);
235 fill_rect(x
-2, y
-2, w
+4, h
+4, fg
);
236 fill_rect(x
-1, y
-1, w
+2, h
+2, blk
);
237 draw_str8(x
, y
, message
, fg
, bg
);
244 ////////////////////////////////////////////////////////////////////////////////
245 static Uint32
timer_frame (Uint32 interval
, void *param
) {
247 evt
.user
.type
= SDL_USEREVENT
;
248 evt
.user
.code
= EVT_SHOW_FRAME
;
254 static Uint32
timer_msghide (Uint32 interval
, void *param
) {
256 evt
.user
.type
= SDL_USEREVENT
;
257 evt
.user
.code
= EVT_MESSAGE_RESET
;
259 tid_msghide
= 0; /* cancelled */
264 static void show_message (const char *msg
) {
265 if (tid_msghide
!= 0) SDL_RemoveTimer(tid_msghide
);
266 if (message
!= NULL
) free(message
);
267 if (msg
&& msg
[0]) message
= strdup(msg
); else message
= NULL
;
268 tid_msghide
= SDL_AddTimer(5000, timer_msghide
, NULL
);
273 static void hide_message (void) {
274 if (message
!= NULL
) {
277 if (tid_msghide
!= 0) SDL_RemoveTimer(tid_msghide
);
284 ////////////////////////////////////////////////////////////////////////////////
285 static void main_loop (void) {
287 if (genglib_load_file(&stklib
, "strokes.dat") == 0) {
288 stroke_list_rebuild();
289 show_message("'strokes.dat' loaded...");
293 while (SDL_WaitEvent(&event
)) {
294 switch (event
.type
) {
295 case SDL_QUIT
: return;
296 case SDL_MOUSEMOTION
:
297 if (SDL_GetWindowGrab(sdl_win
)) {
298 if ((event
.motion
.state
&SDL_BUTTON(1)) != 0) {
299 if (stklist_curptr
== -1) {
300 geng_add_point(&stk
, event
.motion
.x
, event
.motion
.y
);
304 } else if (event
.motion
.state
== 0) {
305 int np
= stroke_list_curptr(event
.motion
.x
, event
.motion
.y
);
306 if (np
!= stklist_curptr
) {
312 case SDL_MOUSEBUTTONDOWN
:
316 if (event
.button
.button
== SDL_BUTTON_LEFT
) {
317 if (stklist_curptr
== -1) {
319 SDL_SetWindowGrab(sdl_win
, SDL_TRUE
);
320 geng_add_point(&stk
, event
.button
.x
, event
.button
.y
);
325 for (int f
= 0; f
< stklib
.count
; ++f
) {
326 if (strcmp(stklib
.names
[f
], stknames
[stklist_curptr
]) == 0) {
327 geng_copytpl(stkpat
, stklib
.tpls
[f
]);
328 curstkname
= stklib
.names
[f
];
334 } else if (event
.button
.button
== SDL_BUTTON_RIGHT
|| event
.button
.button
== SDL_BUTTON_MIDDLE
) {
336 const char *nm
= (event
.button
.button
== SDL_BUTTON_MIDDLE
? NULL
: curstkname
);
337 geng_normalize(stkpat
, &stk
);
341 if ((nm
= read_str()) == READSTR_QUIT
) return;
344 genglib_add_stk(&stklib
, nm
, &stk
);
345 stroke_list_rebuild();
346 stklist_curptr
= stroke_list_curptr(event
.button
.x
, event
.button
.y
);
354 case SDL_MOUSEBUTTONUP
:
355 if (event
.button
.button
== SDL_BUTTON_LEFT
) {
356 if (SDL_GetWindowGrab(sdl_win
)) {
357 SDL_SetWindowGrab(sdl_win
, SDL_FALSE
);
361 curstkname
= genglib_find_match(&stklib
, &stk
, &score
);
362 if (curstkname
!= NULL
&& score
< 2.0) curstkname
= NULL
;
363 if (curstkname
!= NULL
) {
364 snprintf(buf
, sizeof(buf
), "%s: score=%.15g", curstkname
, score
);
366 snprintf(buf
, sizeof(buf
), "can't recognize stroke");
375 switch (event
.key
.keysym
.sym
) {
383 if (!SDL_GetWindowGrab(sdl_win
)) {
389 if (genglib_save_file(&stklib
, "strokes.dat") == 0) show_message("'strokes.dat' saved...");
393 if (genglib_load_file(&stklib
, "strokes.dat") == 0) show_message("'strokes.dat' loaded...");
394 stroke_list_rebuild();
399 genglib_clear(&stklib
);
400 stroke_list_rebuild();
401 show_message("library cleared...");
403 case SDLK_r
: case SDLK_DELETE
: case SDLK_KP_PERIOD
:
404 if (stklist_curptr
>= 0) {
405 genglib_remove(&stklib
, stknames
[stklist_curptr
]);
407 stroke_list_rebuild();
411 case SDLK_F10
: return;
416 switch (event
.user
.code
) {
423 case EVT_MESSAGE_RESET
:
428 case SDL_WINDOWEVENT
:
429 switch (event
.window
.event
) {
430 case SDL_WINDOWEVENT_EXPOSED
:
442 int main (int argc
, char *argv
[]) {
443 if (video_init()) return 1;
444 genglib_init(&stklib
);
446 tid_frame
= SDL_AddTimer(1000/FPS
, timer_frame
, NULL
);
448 SDL_RemoveTimer(tid_frame
);
449 SDL_SetWindowGrab(sdl_win
, SDL_FALSE
);
451 genglib_done(&stklib
);
453 if (message
!= NULL
) free(message
);