| @@ -41,6 +41,17 @@ enum { |
| 41 | 41 | | FNAME_CNT = 1024 |
| 42 | 42 | | }; |
| 43 | 43 | | |
| 44 | + | typedef struct { |
| 45 | + | struct timeval when; |
| 46 | + | Bool active; |
| 47 | + | timeout_f handler; |
| 48 | + | } timeout_t; |
| 49 | + | |
| 50 | + | /* timeout handler functions: */ |
| 51 | + | void redraw(); |
| 52 | + | void hide_cursor(); |
| 53 | + | void animate(); |
| 54 | + | |
| 44 | 55 | | appmode_t mode; |
| 45 | 56 | | img_t img; |
| 46 | 57 | | tns_t tns; |
| @@ -52,9 +63,11 @@ size_t filesize; |
| 52 | 63 | | |
| 53 | 64 | | char win_title[TITLE_LEN]; |
| 54 | 65 | | |
| 55 | | − | int timo_cursor; |
| 56 | | − | int timo_redraw; |
| 57 | | − | int timo_adelay; /* multi-frame animation delay time */ |
| 66 | + | timeout_t timeouts[] = { |
| 67 | + | { { 0, 0 }, False, redraw }, |
| 68 | + | { { 0, 0 }, False, hide_cursor }, |
| 69 | + | { { 0, 0 }, False, animate } |
| 70 | + | }; |
| 58 | 71 | | |
| 59 | 72 | | void cleanup() { |
| 60 | 73 | | static int in = 0; |
| @@ -120,6 +133,54 @@ void remove_file(int n, unsigned char silent) { |
| 120 | 133 | | tns.cnt--; |
| 121 | 134 | | } |
| 122 | 135 | | |
| 136 | + | void set_timeout(timeout_f handler, int time, int overwrite) { |
| 137 | + | int i; |
| 138 | + | |
| 139 | + | for (i = 0; i < LEN(timeouts); i++) { |
| 140 | + | if (timeouts[i].handler == handler) { |
| 141 | + | if (!timeouts[i].active || overwrite) { |
| 142 | + | gettimeofday(&timeouts[i].when, 0); |
| 143 | + | MSEC_ADD_TO_TIMEVAL(time, &timeouts[i].when); |
| 144 | + | timeouts[i].active = True; |
| 145 | + | } |
| 146 | + | return; |
| 147 | + | } |
| 148 | + | } |
| 149 | + | } |
| 150 | + | |
| 151 | + | void reset_timeout(timeout_f handler) { |
| 152 | + | int i; |
| 153 | + | |
| 154 | + | for (i = 0; i < LEN(timeouts); i++) { |
| 155 | + | if (timeouts[i].handler == handler) { |
| 156 | + | timeouts[i].active = False; |
| 157 | + | return; |
| 158 | + | } |
| 159 | + | } |
| 160 | + | } |
| 161 | + | |
| 162 | + | int check_timeouts(struct timeval *t) { |
| 163 | + | int i, tdiff, tmin = -1; |
| 164 | + | struct timeval now; |
| 165 | + | |
| 166 | + | gettimeofday(&now, 0); |
| 167 | + | for (i = 0; i < LEN(timeouts); i++) { |
| 168 | + | if (timeouts[i].active) { |
| 169 | + | tdiff = TIMEDIFF(&timeouts[i].when, &now); |
| 170 | + | if (tdiff <= 0) { |
| 171 | + | timeouts[i].active = False; |
| 172 | + | if (timeouts[i].handler) |
| 173 | + | timeouts[i].handler(); |
| 174 | + | } else if (tmin < 0 || tdiff < tmin) { |
| 175 | + | tmin = tdiff; |
| 176 | + | } |
| 177 | + | } |
| 178 | + | } |
| 179 | + | if (tmin > 0 && t) |
| 180 | + | MSEC_TO_TIMEVAL(tmin, t); |
| 181 | + | return tmin > 0; |
| 182 | + | } |
| 183 | + | |
| 123 | 184 | | void load_image(int new) { |
| 124 | 185 | | struct stat fstats; |
| 125 | 186 | | |
| @@ -144,9 +205,9 @@ void load_image(int new) { |
| 144 | 205 | | |
| 145 | 206 | | if (img.multi.cnt) { |
| 146 | 207 | | if (img.multi.animate) |
| 147 | | − | timo_adelay = img.multi.frames[img.multi.sel].delay; |
| 208 | + | set_timeout(animate, img.multi.frames[img.multi.sel].delay, 1); |
| 148 | 209 | | else |
| 149 | | − | timo_adelay = 0; |
| 210 | + | reset_timeout(animate); |
| 150 | 211 | | } |
| 151 | 212 | | } |
| 152 | 213 | | |
| @@ -186,15 +247,31 @@ void update_title() { |
| 186 | 247 | | void redraw() { |
| 187 | 248 | | if (mode == MODE_IMAGE) { |
| 188 | 249 | | img_render(&img, &win); |
| 189 | | − | if (timo_cursor) |
| 190 | | − | win_set_cursor(&win, CURSOR_ARROW); |
| 191 | | − | else |
| 250 | + | if (img.multi.animate) { |
| 192 | 251 | | win_set_cursor(&win, CURSOR_NONE); |
| 252 | + | } else { |
| 253 | + | win_set_cursor(&win, CURSOR_ARROW); |
| 254 | + | set_timeout(hide_cursor, TO_CURSOR_HIDE, 1); |
| 255 | + | } |
| 193 | 256 | | } else { |
| 194 | 257 | | tns_render(&tns, &win); |
| 195 | 258 | | } |
| 196 | 259 | | update_title(); |
| 197 | | − | timo_redraw = 0; |
| 260 | + | reset_timeout(redraw); |
| 261 | + | } |
| 262 | + | |
| 263 | + | void hide_cursor() { |
| 264 | + | win_set_cursor(&win, CURSOR_NONE); |
| 265 | + | } |
| 266 | + | |
| 267 | + | void animate() { |
| 268 | + | int delay; |
| 269 | + | |
| 270 | + | delay = img_frame_animate(&img, 0); |
| 271 | + | if (delay) { |
| 272 | + | set_timeout(animate, delay, 1); |
| 273 | + | redraw(); |
| 274 | + | } |
| 198 | 275 | | } |
| 199 | 276 | | |
| 200 | 277 | | Bool keymask(const keymap_t *k, unsigned int state) { |
| @@ -233,7 +310,7 @@ void on_buttonpress(XButtonEvent *bev) { |
| 233 | 310 | | |
| 234 | 311 | | if (mode == MODE_IMAGE) { |
| 235 | 312 | | win_set_cursor(&win, CURSOR_ARROW); |
| 236 | | − | timo_cursor = TO_CURSOR_HIDE; |
| 313 | + | set_timeout(hide_cursor, TO_CURSOR_HIDE, 1); |
| 237 | 314 | | |
| 238 | 315 | | for (i = 0; i < LEN(buttons); i++) { |
| 239 | 316 | | if (buttons[i].button == bev->button && |
| @@ -252,7 +329,7 @@ void on_buttonpress(XButtonEvent *bev) { |
| 252 | 329 | | if (sel == tns.sel) { |
| 253 | 330 | | load_image(tns.sel); |
| 254 | 331 | | mode = MODE_IMAGE; |
| 255 | | − | timo_cursor = TO_CURSOR_HIDE; |
| 332 | + | set_timeout(hide_cursor, TO_CURSOR_HIDE, 1); |
| 256 | 333 | | } else { |
| 257 | 334 | | tns_highlight(&tns, &win, tns.sel, False); |
| 258 | 335 | | tns_highlight(&tns, &win, sel, True); |
| @@ -272,75 +349,39 @@ void on_buttonpress(XButtonEvent *bev) { |
| 272 | 349 | | } |
| 273 | 350 | | |
| 274 | 351 | | void run() { |
| 275 | | − | int xfd, timeout; |
| 352 | + | int xfd; |
| 276 | 353 | | fd_set fds; |
| 277 | | − | struct timeval tt, t0, t1; |
| 354 | + | struct timeval timeout; |
| 278 | 355 | | XEvent ev; |
| 279 | 356 | | |
| 280 | | − | timo_cursor = mode == MODE_IMAGE ? TO_CURSOR_HIDE : 0; |
| 281 | | − | |
| 282 | 357 | | redraw(); |
| 283 | 358 | | |
| 284 | 359 | | while (1) { |
| 285 | | − | if (mode == MODE_THUMB && tns.cnt < filecnt) { |
| 360 | + | if (!XPending(win.env.dpy)) { |
| 286 | 361 | | /* load thumbnails */ |
| 287 | | − | win_set_cursor(&win, CURSOR_WATCH); |
| 288 | | − | gettimeofday(&t0, 0); |
| 289 | | − | |
| 290 | | − | while (tns.cnt < filecnt && !XPending(win.env.dpy)) { |
| 362 | + | while (mode == MODE_THUMB && tns.cnt < filecnt) { |
| 363 | + | win_set_cursor(&win, CURSOR_WATCH); |
| 291 | 364 | | if (tns_load(&tns, tns.cnt, &files[tns.cnt], False, False)) |
| 292 | 365 | | tns.cnt++; |
| 293 | 366 | | else |
| 294 | 367 | | remove_file(tns.cnt, 0); |
| 295 | | − | gettimeofday(&t1, 0); |
| 296 | | − | if (TIMEDIFF(&t1, &t0) >= TO_THUMBS_LOAD) |
| 297 | | − | break; |
| 298 | | − | } |
| 299 | | − | if (tns.cnt == filecnt) |
| 300 | | − | win_set_cursor(&win, CURSOR_ARROW); |
| 301 | | − | if (!XPending(win.env.dpy)) { |
| 302 | | − | redraw(); |
| 303 | | − | continue; |
| 304 | | − | } else { |
| 305 | | − | timo_redraw = TO_THUMBS_LOAD; |
| 306 | | − | } |
| 307 | | − | } |
| 308 | | − | |
| 309 | | − | if (timo_cursor || timo_redraw || timo_adelay) { |
| 310 | | − | /* check active timeouts */ |
| 311 | | − | gettimeofday(&t0, 0); |
| 312 | | − | timeout = min_int_nz(3, timo_cursor, timo_redraw, timo_adelay); |
| 313 | | − | MSEC_TO_TIMEVAL(timeout, &tt); |
| 314 | | − | xfd = ConnectionNumber(win.env.dpy); |
| 315 | | − | FD_ZERO(&fds); |
| 316 | | − | FD_SET(xfd, &fds); |
| 317 | | − | |
| 318 | | − | if (!XPending(win.env.dpy)) |
| 319 | | − | select(xfd + 1, &fds, 0, 0, &tt); |
| 320 | | − | gettimeofday(&t1, 0); |
| 321 | | − | timeout = MIN(TIMEDIFF(&t1, &t0), timeout); |
| 322 | | − | |
| 323 | | − | /* timeouts fired? */ |
| 324 | | − | if (timo_cursor) { |
| 325 | | − | timo_cursor = MAX(0, timo_cursor - timeout); |
| 326 | | − | if (!timo_cursor) |
| 327 | | − | win_set_cursor(&win, CURSOR_NONE); |
| 328 | | − | } |
| 329 | | − | if (timo_redraw) { |
| 330 | | − | timo_redraw = MAX(0, timo_redraw - timeout); |
| 331 | | − | if (!timo_redraw) |
| 368 | + | if (tns.cnt == filecnt) { |
| 332 | 369 | | redraw(); |
| 333 | | − | } |
| 334 | | − | if (timo_adelay) { |
| 335 | | − | timo_adelay = MAX(0, timo_adelay - timeout); |
| 336 | | − | if (!timo_adelay) { |
| 337 | | − | if ((timo_adelay = img_frame_animate(&img, 0))) |
| 338 | | − | redraw(); |
| 370 | + | win_set_cursor(&win, CURSOR_ARROW); |
| 371 | + | } else { |
| 372 | + | set_timeout(redraw, TO_REDRAW_THUMBS, 0); |
| 373 | + | check_timeouts(NULL); |
| 339 | 374 | | } |
| 340 | 375 | | } |
| 341 | | − | if ((timo_cursor || timo_redraw || timo_adelay) && |
| 342 | | − | !XPending(win.env.dpy)) |
| 343 | | − | continue; |
| 376 | + | |
| 377 | + | /* handle timeouts */ |
| 378 | + | if (check_timeouts(&timeout)) { |
| 379 | + | xfd = ConnectionNumber(win.env.dpy); |
| 380 | + | FD_ZERO(&fds); |
| 381 | + | FD_SET(xfd, &fds); |
| 382 | + | if (!select(xfd + 1, &fds, 0, 0, &timeout)) |
| 383 | + | check_timeouts(NULL); |
| 384 | + | } |
| 344 | 385 | | } |
| 345 | 386 | | |
| 346 | 387 | | if (!XNextEvent(win.env.dpy, &ev)) { |
| @@ -355,7 +396,7 @@ void run() { |
| 355 | 396 | | break; |
| 356 | 397 | | case ConfigureNotify: |
| 357 | 398 | | if (win_configure(&win, &ev.xconfigure)) { |
| 358 | | − | timo_redraw = TO_WIN_RESIZE; |
| 399 | + | set_timeout(redraw, TO_REDRAW_RESIZE, 0); |
| 359 | 400 | | if (mode == MODE_IMAGE) |
| 360 | 401 | | img.checkpan = 1; |
| 361 | 402 | | else |
| @@ -366,9 +407,10 @@ void run() { |
| 366 | 407 | | on_keypress(&ev.xkey); |
| 367 | 408 | | break; |
| 368 | 409 | | case MotionNotify: |
| 369 | | − | if (!timo_cursor) |
| 410 | + | if (mode == MODE_IMAGE) { |
| 370 | 411 | | win_set_cursor(&win, CURSOR_ARROW); |
| 371 | | − | timo_cursor = TO_CURSOR_HIDE; |
| 412 | + | set_timeout(hide_cursor, TO_CURSOR_HIDE, 1); |
| 413 | + | } |
| 372 | 414 | | break; |
| 373 | 415 | | } |
| 374 | 416 | | } |