playanim: make alphanumeric +/- and numpad +/- work the same way.
[blender.git] / source / blender / windowmanager / intern / wm_playanim.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Campbell Barton
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/windowmanager/intern/wm_playanim.c
29  *  \ingroup wm
30  *
31  * \note This file uses ghost directly and none of the WM definitions.
32  *       this could be made into its own module, alongside creator/
33  */
34
35 #include <sys/types.h>
36 #include <fcntl.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <math.h>
40
41 #ifndef WIN32
42 #  include <unistd.h>
43 #  include <sys/times.h>
44 #  include <sys/wait.h>
45 #else
46 #  include <io.h>
47 #endif
48 #include "MEM_guardedalloc.h"
49
50 #include "PIL_time.h"
51
52 #include "BLI_fileops.h"
53 #include "BLI_listbase.h"
54 #include "BLI_path_util.h"
55 #include "BLI_rect.h"
56 #include "BLI_string.h"
57 #include "BLI_utildefines.h"
58
59 #include "IMB_imbuf_types.h"
60 #include "IMB_imbuf.h"
61
62 #include "BKE_blender.h"
63 #include "BKE_global.h"
64 #include "BKE_image.h"
65
66 #include "BIF_gl.h"
67 #include "BIF_glutil.h"
68
69 #include "DNA_scene_types.h"
70 #include "ED_datafiles.h" /* for fonts */
71 #include "GHOST_C-api.h"
72 #include "BLF_api.h"
73
74 #include "wm_event_types.h"
75
76 #include "WM_api.h"  /* only for WM_main_playanim */
77
78 struct PlayState;
79 static void playanim_window_zoom(const struct PlayState *ps, const float zoom_offset);
80
81 typedef struct PlayState {
82
83         /* playback state */
84         short direction;
85         short next;
86         short once;
87         short turbo;
88         short pingpong;
89         short noskip;
90         short sstep;
91         short pause;
92         short wait2;
93         short stopped;
94         short go;
95
96         int fstep;
97
98         /* current picture */
99         struct PlayAnimPict *picture;
100
101         /* set once at the start */
102         int ibufx, ibufy;
103         int fontid;
104
105         /* saves passing args */
106         struct ImBuf *curframe_ibuf;
107 } PlayState;
108
109 /* for debugging */
110 #if 0
111 void print_ps(PlayState *ps)
112 {
113         printf("ps:\n");
114         printf("    direction=%d,\n", (int)ps->direction);
115         printf("    next=%d,\n", ps->next);
116         printf("    once=%d,\n", ps->once);
117         printf("    turbo=%d,\n", ps->turbo);
118         printf("    pingpong=%d,\n", ps->pingpong);
119         printf("    noskip=%d,\n", ps->noskip);
120         printf("    sstep=%d,\n", ps->sstep);
121         printf("    pause=%d,\n", ps->pause);
122         printf("    wait2=%d,\n", ps->wait2);
123         printf("    stopped=%d,\n", ps->stopped);
124         printf("    go=%d,\n\n", ps->go);
125         fflush(stdout);
126 }
127 #endif
128
129 /* global for window and events */
130 typedef enum eWS_Qual {
131         WS_QUAL_LSHIFT  = (1 << 0),
132         WS_QUAL_RSHIFT  = (1 << 1),
133         WS_QUAL_SHIFT   = (WS_QUAL_LSHIFT | WS_QUAL_RSHIFT),
134         WS_QUAL_LALT    = (1 << 2),
135         WS_QUAL_RALT    = (1 << 3),
136         WS_QUAL_ALT     = (WS_QUAL_LALT | WS_QUAL_RALT),
137         WS_QUAL_LCTRL   = (1 << 4),
138         WS_QUAL_RCTRL   = (1 << 5),
139         WS_QUAL_CTRL    = (WS_QUAL_LCTRL | WS_QUAL_RCTRL),
140         WS_QUAL_LMOUSE  = (1 << 16),
141         WS_QUAL_MMOUSE  = (1 << 17),
142         WS_QUAL_RMOUSE  = (1 << 18),
143         WS_QUAL_MOUSE   = (WS_QUAL_LMOUSE | WS_QUAL_MMOUSE | WS_QUAL_RMOUSE)
144 } eWS_Qual;
145
146 static struct WindowStateGlobal {
147         GHOST_SystemHandle ghost_system;
148         void *ghost_window;
149
150         /* events */
151         eWS_Qual qual;
152 } g_WS = {NULL};
153
154 static void playanim_window_get_size(int *width_r, int *height_r)
155 {
156         GHOST_RectangleHandle bounds = GHOST_GetClientBounds(g_WS.ghost_window);
157         *width_r = GHOST_GetWidthRectangle(bounds);
158         *height_r = GHOST_GetHeightRectangle(bounds);
159         GHOST_DisposeRectangle(bounds);
160 }
161
162 /* implementation */
163 static void playanim_event_qual_update(void)
164 {
165         int val;
166
167         /* Shift */
168         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftShift, &val);
169         if (val) g_WS.qual |=  WS_QUAL_LSHIFT;
170         else     g_WS.qual &= ~WS_QUAL_LSHIFT;
171
172         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightShift, &val);
173         if (val) g_WS.qual |=  WS_QUAL_RSHIFT;
174         else     g_WS.qual &= ~WS_QUAL_RSHIFT;
175
176         /* Control */
177         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftControl, &val);
178         if (val) g_WS.qual |=  WS_QUAL_LCTRL;
179         else     g_WS.qual &= ~WS_QUAL_LCTRL;
180
181         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightControl, &val);
182         if (val) g_WS.qual |=  WS_QUAL_RCTRL;
183         else     g_WS.qual &= ~WS_QUAL_RCTRL;
184
185         /* Alt */
186         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyLeftAlt, &val);
187         if (val) g_WS.qual |=  WS_QUAL_LALT;
188         else     g_WS.qual &= ~WS_QUAL_LALT;
189
190         GHOST_GetModifierKeyState(g_WS.ghost_system, GHOST_kModifierKeyRightAlt, &val);
191         if (val) g_WS.qual |=  WS_QUAL_RALT;
192         else     g_WS.qual &= ~WS_QUAL_RALT;
193
194         /* LMB */
195         GHOST_GetButtonState(g_WS.ghost_system, GHOST_kButtonMaskLeft, &val);
196         if (val) g_WS.qual |=  WS_QUAL_LMOUSE;
197         else     g_WS.qual &= ~WS_QUAL_LMOUSE;
198
199         /* MMB */
200         GHOST_GetButtonState(g_WS.ghost_system, GHOST_kButtonMaskMiddle, &val);
201         if (val) g_WS.qual |=  WS_QUAL_MMOUSE;
202         else     g_WS.qual &= ~WS_QUAL_MMOUSE;
203
204         /* RMB */
205         GHOST_GetButtonState(g_WS.ghost_system, GHOST_kButtonMaskRight, &val);
206         if (val) g_WS.qual |=  WS_QUAL_RMOUSE;
207         else     g_WS.qual &= ~WS_QUAL_RMOUSE;
208 }
209
210 typedef struct PlayAnimPict {
211         struct PlayAnimPict *next, *prev;
212         char *mem;
213         int size;
214         char *name;
215         struct ImBuf *ibuf;
216         struct anim *anim;
217         int frame;
218         int IB_flags;
219 } PlayAnimPict;
220
221 static struct ListBase picsbase = {NULL, NULL};
222 static int fromdisk = FALSE;
223 static float zoomx = 1.0, zoomy = 1.0;
224 static double ptottime = 0.0, swaptime = 0.04;
225
226 static int pupdate_time(void)
227 {
228         static double ltime;
229         double time;
230
231         time = PIL_check_seconds_timer();
232
233         ptottime += (time - ltime);
234         ltime = time;
235         return (ptottime < 0);
236 }
237
238 static void playanim_toscreen(PlayAnimPict *picture, struct ImBuf *ibuf, int fontid, int fstep)
239 {
240
241         if (ibuf == NULL) {
242                 printf("%s: no ibuf for picture '%s'\n", __func__, picture ? picture->name : "<NIL>");
243                 return;
244         }
245         if (ibuf->rect == NULL && ibuf->rect_float) {
246                 IMB_rect_from_float(ibuf);
247                 imb_freerectfloatImBuf(ibuf);
248         }
249         if (ibuf->rect == NULL)
250                 return;
251
252         GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
253
254         glRasterPos2f(0.0f, 0.0f);
255
256         glDrawPixels(ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
257
258         pupdate_time();
259
260         if (picture && (g_WS.qual & (WS_QUAL_SHIFT | WS_QUAL_LMOUSE)) && (fontid != -1)) {
261                 int sizex, sizey;
262                 float fsizex_inv, fsizey_inv;
263                 char str[32 + FILE_MAX];
264                 cpack(-1);
265                 BLI_snprintf(str, sizeof(str), "%s | %.2f frames/s", picture->name, fstep / swaptime);
266
267                 playanim_window_get_size(&sizex, &sizey);
268                 fsizex_inv = 1.0f / sizex;
269                 fsizey_inv = 1.0f / sizey;
270
271                 BLF_enable(fontid, BLF_ASPECT);
272                 BLF_aspect(fontid, fsizex_inv, fsizey_inv, 1.0f);
273                 BLF_position(fontid, 10.0f * fsizex_inv, 10.0f * fsizey_inv, 0.0f);
274                 BLF_draw(fontid, str, sizeof(str));
275         }
276
277         GHOST_SwapWindowBuffers(g_WS.ghost_window);
278 }
279
280 static void build_pict_list(char *first, int totframes, int fstep, int fontid)
281 {
282         char *mem, filepath[FILE_MAX];
283 //      short val;
284         PlayAnimPict *picture = NULL;
285         struct ImBuf *ibuf = NULL;
286         char str[32 + FILE_MAX];
287         struct anim *anim;
288
289         if (IMB_isanim(first)) {
290                 /* OCIO_TODO: support different input color space */
291                 anim = IMB_open_anim(first, IB_rect, 0, NULL);
292                 if (anim) {
293                         int pic;
294                         ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
295                         if (ibuf) {
296                                 playanim_toscreen(NULL, ibuf, fontid, fstep);
297                                 IMB_freeImBuf(ibuf);
298                         }
299
300                         for (pic = 0; pic < IMB_anim_get_duration(anim, IMB_TC_NONE); pic++) {
301                                 picture = (PlayAnimPict *)MEM_callocN(sizeof(PlayAnimPict), "Pict");
302                                 picture->anim = anim;
303                                 picture->frame = pic;
304                                 picture->IB_flags = IB_rect;
305                                 BLI_snprintf(str, sizeof(str), "%s : %4.d", first, pic + 1);
306                                 picture->name = strdup(str);
307                                 BLI_addtail(&picsbase, picture);
308                         }
309                 }
310                 else {
311                         printf("couldn't open anim %s\n", first);
312                 }
313         }
314         else {
315                 int count = 0;
316
317                 BLI_strncpy(filepath, first, sizeof(filepath));
318
319                 pupdate_time();
320                 ptottime = 1.0;
321
322                 /* O_DIRECT
323                  *
324                  * If set, all reads and writes on the resulting file descriptor will
325                  * be performed directly to or from the user program buffer, provided
326                  * appropriate size and alignment restrictions are met.  Refer to the
327                  * F_SETFL and F_DIOINFO commands in the fcntl(2) manual entry for
328                  * information about how to determine the alignment constraints.
329                  * O_DIRECT is a Silicon Graphics extension and is only supported on
330                  * local EFS and XFS file systems.
331                  */
332
333                 while (IMB_ispic(filepath) && totframes) {
334                         size_t size;
335                         int file;
336
337                         file = open(filepath, O_BINARY | O_RDONLY, 0);
338                         if (file < 0) {
339                                 /* print errno? */
340                                 return;
341                         }
342
343                         picture = (PlayAnimPict *)MEM_callocN(sizeof(PlayAnimPict), "picture");
344                         if (picture == NULL) {
345                                 printf("Not enough memory for pict struct '%s'\n", filepath);
346                                 close(file);
347                                 return;
348                         }
349                         size = BLI_file_descriptor_size(file);
350
351                         if (size < 1) {
352                                 close(file);
353                                 MEM_freeN(picture);
354                                 return;
355                         }
356
357                         picture->size = size;
358                         picture->IB_flags = IB_rect;
359
360                         if (fromdisk == FALSE) {
361                                 mem = (char *)MEM_mallocN(size, "build pic list");
362                                 if (mem == NULL) {
363                                         printf("Couldn't get memory\n");
364                                         close(file);
365                                         MEM_freeN(picture);
366                                         return;
367                                 }
368
369                                 if (read(file, mem, size) != size) {
370                                         printf("Error while reading %s\n", filepath);
371                                         close(file);
372                                         MEM_freeN(picture);
373                                         MEM_freeN(mem);
374                                         return;
375                                 }
376                         }
377                         else {
378                                 mem = NULL;
379                         }
380
381                         picture->mem = mem;
382                         picture->name = strdup(filepath);
383                         close(file);
384                         BLI_addtail(&picsbase, picture);
385                         count++;
386
387                         pupdate_time();
388
389                         if (ptottime > 1.0) {
390                                 /* OCIO_TODO: support different input color space */
391                                 if (picture->mem) {
392                                         ibuf = IMB_ibImageFromMemory((unsigned char *)picture->mem, picture->size,
393                                                                      picture->IB_flags, NULL, picture->name);
394                                 }
395                                 else {
396                                         ibuf = IMB_loadiffname(picture->name, picture->IB_flags, NULL);
397                                 }
398                                 if (ibuf) {
399                                         playanim_toscreen(picture, ibuf, fontid, fstep);
400                                         IMB_freeImBuf(ibuf);
401                                 }
402                                 pupdate_time();
403                                 ptottime = 0.0;
404                         }
405
406                         BLI_newname(filepath, +fstep);
407
408 #if 0 // XXX25
409                         while (qtest()) {
410                                 switch (qreadN(&val)) {
411                                         case ESCKEY:
412                                                 if (val) return;
413                                                 break;
414                                 }
415                         }
416 #endif
417                         totframes--;
418                 }
419         }
420         return;
421 }
422
423 static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr ps_void)
424 {
425         PlayState *ps = (PlayState *)ps_void;
426         GHOST_TEventType type = GHOST_GetEventType(evt);
427         int val;
428
429         // print_ps(ps);
430
431         playanim_event_qual_update();
432
433         /* convert ghost event into value keyboard or mouse */
434         val = ELEM(type, GHOST_kEventKeyDown, GHOST_kEventButtonDown);
435
436         if (ps->wait2 && ps->stopped) {
437                 ps->stopped = FALSE;
438         }
439
440         if (ps->wait2) {
441                 pupdate_time();
442                 ptottime = 0;
443         }
444
445         switch (type) {
446                 case GHOST_kEventKeyDown:
447                 case GHOST_kEventKeyUp:
448                 {
449                         GHOST_TEventKeyData *key_data;
450
451                         key_data = (GHOST_TEventKeyData *)GHOST_GetEventData(evt);
452                         switch (key_data->key) {
453                                 case GHOST_kKeyA:
454                                         if (val) ps->noskip = !ps->noskip;
455                                         break;
456                                 case GHOST_kKeyP:
457                                         if (val) ps->pingpong = !ps->pingpong;
458                                         break;
459                                 case GHOST_kKeyNumpad1:
460                                         if (val) swaptime = ps->fstep / 60.0;
461                                         break;
462                                 case GHOST_kKeyNumpad2:
463                                         if (val) swaptime = ps->fstep / 50.0;
464                                         break;
465                                 case GHOST_kKeyNumpad3:
466                                         if (val) swaptime = ps->fstep / 30.0;
467                                         break;
468                                 case GHOST_kKeyNumpad4:
469                                         if (g_WS.qual & WS_QUAL_SHIFT)
470                                                 swaptime = ps->fstep / 24.0;
471                                         else
472                                                 swaptime = ps->fstep / 25.0;
473                                         break;
474                                 case GHOST_kKeyNumpad5:
475                                         if (val) swaptime = ps->fstep / 20.0;
476                                         break;
477                                 case GHOST_kKeyNumpad6:
478                                         if (val) swaptime = ps->fstep / 15.0;
479                                         break;
480                                 case GHOST_kKeyNumpad7:
481                                         if (val) swaptime = ps->fstep / 12.0;
482                                         break;
483                                 case GHOST_kKeyNumpad8:
484                                         if (val) swaptime = ps->fstep / 10.0;
485                                         break;
486                                 case GHOST_kKeyNumpad9:
487                                         if (val) swaptime = ps->fstep / 6.0;
488                                         break;
489                                 case GHOST_kKeyLeftArrow:
490                                         if (val) {
491                                                 ps->sstep = TRUE;
492                                                 ps->wait2 = FALSE;
493                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
494                                                         ps->picture = picsbase.first;
495                                                         ps->next = 0;
496                                                 }
497                                                 else {
498                                                         ps->next = -1;
499                                                 }
500                                         }
501                                         break;
502                                 case GHOST_kKeyDownArrow:
503                                         if (val) {
504                                                 ps->wait2 = FALSE;
505                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
506                                                         ps->next = ps->direction = -1;
507                                                 }
508                                                 else {
509                                                         ps->next = -10;
510                                                         ps->sstep = TRUE;
511                                                 }
512                                         }
513                                         break;
514                                 case GHOST_kKeyRightArrow:
515                                         if (val) {
516                                                 ps->sstep = TRUE;
517                                                 ps->wait2 = FALSE;
518                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
519                                                         ps->picture = picsbase.last;
520                                                         ps->next = 0;
521                                                 }
522                                                 else {
523                                                         ps->next = 1;
524                                                 }
525                                         }
526                                         break;
527                                 case GHOST_kKeyUpArrow:
528                                         if (val) {
529                                                 ps->wait2 = FALSE;
530                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
531                                                         ps->next = ps->direction = 1;
532                                                 }
533                                                 else {
534                                                         ps->next = 10;
535                                                         ps->sstep = TRUE;
536                                                 }
537                                         }
538                                         break;
539
540                                 case GHOST_kKeySlash:
541                                 case GHOST_kKeyNumpadSlash:
542                                         if (val) {
543                                                 if (g_WS.qual & WS_QUAL_SHIFT) {
544                                                         if (ps->curframe_ibuf)
545                                                                 printf(" Name: %s | Speed: %.2f frames/s\n", ps->curframe_ibuf->name, ps->fstep / swaptime);
546                                                 }
547                                                 else {
548                                                         swaptime = ps->fstep / 5.0;
549                                                 }
550                                         }
551                                         break;
552                                 case GHOST_kKeyNumpad0:
553                                         if (val) {
554                                                 if (ps->once) {
555                                                         ps->once = ps->wait2 = FALSE;
556                                                 }
557                                                 else {
558                                                         ps->picture = NULL;
559                                                         ps->once = TRUE;
560                                                         ps->wait2 = FALSE;
561                                                 }
562                                         }
563                                         break;
564                                 case GHOST_kKeyEnter:
565                                 case GHOST_kKeyNumpadEnter:
566                                         if (val) {
567                                                 ps->wait2 = ps->sstep = FALSE;
568                                         }
569                                         break;
570                                 case GHOST_kKeyNumpadPeriod:
571                                         if (val) {
572                                                 if (ps->sstep) ps->wait2 = FALSE;
573                                                 else {
574                                                         ps->sstep = TRUE;
575                                                         ps->wait2 = !ps->wait2;
576                                                 }
577                                         }
578                                         break;
579                                 case GHOST_kKeyEqual:
580                                 case GHOST_kKeyNumpadPlus:
581                                 {
582                                         if (val == 0) break;
583                                         if (g_WS.qual & WS_QUAL_CTRL) {
584                                                 playanim_window_zoom(ps, 1.0f);
585                                         }
586                                         else {
587                                                 swaptime /= 1.1;
588                                         }
589                                         break;
590                                 }
591                                 case GHOST_kKeyMinus:
592                                 case GHOST_kKeyNumpadMinus:
593                                 {
594                                         if (val == 0) break;
595                                         if (g_WS.qual & WS_QUAL_CTRL) {
596                                                 playanim_window_zoom(ps, -1.0f);
597                                         }
598                                         else {
599                                                 swaptime *= 1.1;
600                                         }
601                                         break;
602                                 }
603                                 case GHOST_kKeyEsc:
604                                         ps->go = FALSE;
605                                         break;
606                                 default:
607                                         break;
608                         }
609                         break;
610                 }
611                 case GHOST_kEventCursorMove:
612                 {
613                         if (g_WS.qual & WS_QUAL_LMOUSE) {
614                                 int sizex, sizey;
615                                 int i;
616
617                                 GHOST_TEventCursorData *cd = GHOST_GetEventData(evt);
618                                 int cx, cy;
619
620                                 GHOST_ScreenToClient(g_WS.ghost_window, cd->x, cd->y, &cx, &cy);
621
622                                 playanim_window_get_size(&sizex, &sizey);
623                                 ps->picture = picsbase.first;
624                                 /* TODO - store in ps direct? */
625                                 i = 0;
626                                 while (ps->picture) {
627                                         i++;
628                                         ps->picture = ps->picture->next;
629                                 }
630                                 i = (i * cx) / sizex;
631                                 ps->picture = picsbase.first;
632                                 for (; i > 0; i--) {
633                                         if (ps->picture->next == NULL) break;
634                                         ps->picture = ps->picture->next;
635                                 }
636                                 ps->sstep = TRUE;
637                                 ps->wait2 = FALSE;
638                                 ps->next = 0;
639                         }
640                         break;
641                 }
642                 case GHOST_kEventWindowSize:
643                 case GHOST_kEventWindowMove:
644                 {
645                         int sizex, sizey;
646
647                         playanim_window_get_size(&sizex, &sizey);
648                         GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
649
650                         glViewport(0, 0, sizex, sizey);
651                         glScissor(0, 0, sizex, sizey);
652
653                         zoomx = (float) sizex / ps->ibufx;
654                         zoomy = (float) sizey / ps->ibufy;
655                         zoomx = floor(zoomx + 0.5f);
656                         zoomy = floor(zoomy + 0.5f);
657                         if (zoomx < 1.0f) zoomx = 1.0f;
658                         if (zoomy < 1.0f) zoomy = 1.0f;
659
660                         sizex = zoomx * ps->ibufx;
661                         sizey = zoomy * ps->ibufy;
662
663                         glPixelZoom(zoomx, zoomy);
664                         glEnable(GL_DITHER);
665                         ptottime = 0.0;
666                         playanim_toscreen(ps->picture, ps->curframe_ibuf, ps->fontid, ps->fstep);
667
668                         break;
669                 }
670                 case GHOST_kEventQuit:
671                 case GHOST_kEventWindowClose:
672                 {
673                         ps->go = FALSE;
674                         break;
675                 }
676                 default:
677                         /* quiet warnings */
678                         break;
679         }
680
681         return 1;
682 }
683
684 static void playanim_window_open(const char *title, int posx, int posy, int sizex, int sizey, int start_maximized)
685 {
686         GHOST_TWindowState inital_state;
687         GHOST_TUns32 scr_w, scr_h;
688
689         GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &scr_w, &scr_h);
690
691         posy = (scr_h - posy - sizey);
692
693         if (start_maximized == G_WINDOWSTATE_FULLSCREEN)
694                 inital_state = start_maximized ? GHOST_kWindowStateFullScreen : GHOST_kWindowStateNormal;
695         else
696                 inital_state = start_maximized ? GHOST_kWindowStateMaximized : GHOST_kWindowStateNormal;
697
698         g_WS.ghost_window = GHOST_CreateWindow(g_WS.ghost_system,
699                                                title,
700                                                posx, posy, sizex, sizey,
701                                                inital_state,
702                                                GHOST_kDrawingContextTypeOpenGL,
703                                                FALSE /* no stereo */, FALSE);
704 }
705
706 static void playanim_window_zoom(const PlayState *ps, const float zoom_offset)
707 {
708         int sizex, sizey;
709         /* int ofsx, ofsy; */ /* UNUSED */
710
711         if (zoomx + zoom_offset > 0.0f) zoomx += zoom_offset;
712         if (zoomy + zoom_offset > 0.0f) zoomy += zoom_offset;
713
714         // playanim_window_get_position(&ofsx, &ofsy);
715         playanim_window_get_size(&sizex, &sizey);
716         /* ofsx += sizex / 2; */ /* UNUSED */
717         /* ofsy += sizey / 2; */ /* UNUSED */
718         sizex = zoomx * ps->ibufx;
719         sizey = zoomy * ps->ibufy;
720         /* ofsx -= sizex / 2; */ /* UNUSED */
721         /* ofsy -= sizey / 2; */ /* UNUSED */
722         // window_set_position(g_WS.ghost_window,sizex,sizey);
723         GHOST_SetClientSize(g_WS.ghost_window, sizex, sizey);
724 }
725
726 void WM_main_playanim(int argc, const char **argv)
727 {
728         struct ImBuf *ibuf = NULL;
729         char filepath[FILE_MAX];
730         GHOST_TUns32 maxwinx, maxwiny;
731         /* short c233 = FALSE, yuvx = FALSE; */ /* UNUSED */
732         int i;
733         /* This was done to disambiguate the name for use under c++. */
734         struct anim *anim = NULL;
735         int start_x = 0, start_y = 0;
736         int sfra = -1;
737         int efra = -1;
738         int totblock;
739
740         PlayState ps = {0};
741
742         /* ps.doubleb   = TRUE;*/ /* UNUSED */
743         ps.go        = TRUE;
744         ps.direction = TRUE;
745         ps.next      = 1;
746         ps.once      = FALSE;
747         ps.turbo     = FALSE;
748         ps.pingpong  = FALSE;
749         ps.noskip    = FALSE;
750         ps.sstep     = FALSE;
751         ps.pause     = FALSE;
752         ps.wait2     = FALSE;
753         ps.stopped   = FALSE;
754         ps.picture   = NULL;
755         /* resetmap = FALSE */
756
757         ps.fstep     = 1;
758
759         ps.fontid = -1;
760
761         while (argc > 1) {
762                 if (argv[1][0] == '-') {
763                         switch (argv[1][1]) {
764                                 case 'm':
765                                         fromdisk = TRUE;
766                                         break;
767                                 case 'p':
768                                         if (argc > 3) {
769                                                 start_x = atoi(argv[2]);
770                                                 start_y = atoi(argv[3]);
771                                                 argc -= 2;
772                                                 argv += 2;
773                                         }
774                                         else {
775                                                 printf("too few arguments for -p (need 2): skipping\n");
776                                         }
777                                         break;
778                                 case 'f':
779                                         if (argc > 3) {
780                                                 double fps = atof(argv[2]);
781                                                 double fps_base = atof(argv[3]);
782                                                 if (fps == 0.0) {
783                                                         fps = 1;
784                                                         printf("invalid fps,"
785                                                                "forcing 1\n");
786                                                 }
787                                                 swaptime = fps_base / fps;
788                                                 argc -= 2;
789                                                 argv += 2;
790                                         }
791                                         else {
792                                                 printf("too few arguments for -f (need 2): skipping\n");
793                                         }
794                                         break;
795                                 case 's':
796                                         sfra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) ));
797                                         argc--;
798                                         argv++;
799                                         break;
800                                 case 'e':
801                                         efra = MIN2(MAXFRAME, MAX2(1, atoi(argv[2]) ));
802                                         argc--;
803                                         argv++;
804                                         break;
805                                 case 'j':
806                                         ps.fstep = MIN2(MAXFRAME, MAX2(1, atoi(argv[2])));
807                                         swaptime *= ps.fstep;
808                                         argc--;
809                                         argv++;
810                                         break;
811                                 default:
812                                         printf("unknown option '%c': skipping\n", argv[1][1]);
813                                         break;
814                         }
815                         argc--;
816                         argv++;
817                 }
818                 else {
819                         break;
820                 }
821         }
822
823         if (argc > 1) {
824                 BLI_strncpy(filepath, argv[1], sizeof(filepath));
825         }
826         else {
827                 BLI_current_working_dir(filepath, sizeof(filepath));
828                 BLI_add_slash(filepath);
829         }
830
831         if (IMB_isanim(filepath)) {
832                 /* OCIO_TODO: support different input color spaces */
833                 anim = IMB_open_anim(filepath, IB_rect, 0, NULL);
834                 if (anim) {
835                         ibuf = IMB_anim_absolute(anim, 0, IMB_TC_NONE, IMB_PROXY_NONE);
836                         IMB_close_anim(anim);
837                         anim = NULL;
838                 }
839         }
840         else if (!IMB_ispic(filepath)) {
841                 exit(1);
842         }
843
844         if (ibuf == NULL) {
845                 /* OCIO_TODO: support different input color space */
846                 ibuf = IMB_loadiffname(filepath, IB_rect, NULL);
847         }
848
849         if (ibuf == NULL) {
850                 printf("couldn't open %s\n", filepath);
851                 exit(1);
852         }
853
854 #if 0 //XXX25
855         #if !defined(WIN32) && !defined(__APPLE__)
856         if (fork()) exit(0);
857         #endif
858 #endif //XXX25
859
860         /* XXX, fixme zr */
861         {
862 //              extern void add_to_mainqueue(wmWindow *win, void *user_data, short evt, short val, char ascii);
863
864                 GHOST_EventConsumerHandle consumer = GHOST_CreateEventConsumer(ghost_event_proc, &ps);
865
866                 g_WS.ghost_system = GHOST_CreateSystem();
867                 GHOST_AddEventConsumer(g_WS.ghost_system, consumer);
868
869
870
871                 playanim_window_open("Blender:Anim", start_x, start_y, ibuf->x, ibuf->y, 0);
872 //XXX25         window_set_handler(g_WS.ghost_window, add_to_mainqueue, NULL);
873
874                 glMatrixMode(GL_PROJECTION);
875                 glLoadIdentity();
876                 glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
877                 glMatrixMode(GL_MODELVIEW);
878         }
879
880         GHOST_GetMainDisplayDimensions(g_WS.ghost_system, &maxwinx, &maxwiny);
881
882         //GHOST_ActivateWindowDrawingContext(g_WS.ghost_window);
883
884         /* initialize the font */
885         BLF_init(11, 72);
886         ps.fontid = BLF_load_mem("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
887         BLF_size(ps.fontid, 11, 72);
888
889         ps.ibufx = ibuf->x;
890         ps.ibufy = ibuf->y;
891
892         if (maxwinx % ibuf->x) maxwinx = ibuf->x * (1 + (maxwinx / ibuf->x));
893         if (maxwiny % ibuf->y) maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y));
894
895         glClearColor(0.0, 0.0, 0.0, 0.0);
896         glClear(GL_COLOR_BUFFER_BIT);
897
898         GHOST_SwapWindowBuffers(g_WS.ghost_window);
899
900         if (sfra == -1 || efra == -1) {
901                 /* one of the frames was invalid, just use all images */
902                 sfra = 1;
903                 efra = MAXFRAME;
904         }
905
906         build_pict_list(filepath, (efra - sfra) + 1, ps.fstep, ps.fontid);
907
908         for (i = 2; i < argc; i++) {
909                 BLI_strncpy(filepath, argv[i], sizeof(filepath));
910                 build_pict_list(filepath, (efra - sfra) + 1, ps.fstep, ps.fontid);
911         }
912
913         IMB_freeImBuf(ibuf);
914         ibuf = NULL;
915
916         pupdate_time();
917         ptottime = 0;
918
919         /* newly added in 2.6x, without this images never get freed */
920 #define USE_IMB_CACHE
921
922         while (ps.go) {
923                 if (ps.pingpong)
924                         ps.direction = -ps.direction;
925
926                 if (ps.direction == 1) {
927                         ps.picture = picsbase.first;
928                 }
929                 else {
930                         ps.picture = picsbase.last;
931                 }
932
933                 if (ps.picture == NULL) {
934                         printf("couldn't find pictures\n");
935                         ps.go = FALSE;
936                 }
937                 if (ps.pingpong) {
938                         if (ps.direction == 1) {
939                                 ps.picture = ps.picture->next;
940                         }
941                         else {
942                                 ps.picture = ps.picture->prev;
943                         }
944                 }
945                 if (ptottime > 0.0) ptottime = 0.0;
946
947                 while (ps.picture) {
948                         int hasevent;
949 #ifndef USE_IMB_CACHE
950                         if (ibuf != NULL && ibuf->ftype == 0) IMB_freeImBuf(ibuf);
951 #endif
952                         if (ps.picture->ibuf) {
953                                 ibuf = ps.picture->ibuf;
954                         }
955                         else if (ps.picture->anim) {
956                                 ibuf = IMB_anim_absolute(ps.picture->anim, ps.picture->frame, IMB_TC_NONE, IMB_PROXY_NONE);
957                         }
958                         else if (ps.picture->mem) {
959                                 /* use correct colorspace here */
960                                 ibuf = IMB_ibImageFromMemory((unsigned char *) ps.picture->mem, ps.picture->size,
961                                                              ps.picture->IB_flags, NULL, ps.picture->name);
962                         }
963                         else {
964                                 /* use correct colorspace here */
965                                 ibuf = IMB_loadiffname(ps.picture->name, ps.picture->IB_flags, NULL);
966                         }
967
968                         if (ibuf) {
969
970 #ifdef USE_IMB_CACHE
971                                 ps.picture->ibuf = ibuf;
972 #endif
973
974                                 BLI_strncpy(ibuf->name, ps.picture->name, sizeof(ibuf->name));
975
976                                 /* why only windows? (from 2.4x) - campbell */
977 #ifdef _WIN32
978                                 GHOST_SetTitle(g_WS.ghost_window, ps.picture->name);
979 #endif
980
981                                 while (pupdate_time()) PIL_sleep_ms(1);
982                                 ptottime -= swaptime;
983                                 playanim_toscreen(ps.picture, ibuf, ps.fontid, ps.fstep);
984                         } /* else deleten */
985                         else {
986                                 printf("error: can't play this image type\n");
987                                 exit(0);
988                         }
989
990                         if (ps.once) {
991                                 if (ps.picture->next == NULL) {
992                                         ps.wait2 = TRUE;
993                                 }
994                                 else if (ps.picture->prev == NULL) {
995                                         ps.wait2 = TRUE;
996                                 }
997                         }
998
999                         ps.next = ps.direction;
1000
1001
1002                         while ((hasevent = GHOST_ProcessEvents(g_WS.ghost_system, 0) || ps.wait2 != 0)) {
1003                                 if (hasevent) {
1004                                         GHOST_DispatchEvents(g_WS.ghost_system);
1005                                 }
1006                                 if (ps.wait2) {
1007                                         if (hasevent) {
1008                                                 if (ibuf) {
1009                                                         while (pupdate_time()) PIL_sleep_ms(1);
1010                                                         ptottime -= swaptime;
1011                                                         playanim_toscreen(ps.picture, ibuf, ps.fontid, ps.fstep);
1012                                                 }
1013                                         }
1014                                 }
1015                                 if (!ps.go) {
1016                                         break;
1017                                 }
1018                         }
1019
1020                         ps.wait2 = ps.sstep;
1021
1022                         if (ps.wait2 == 0 && ps.stopped == 0) {
1023                                 ps.stopped = TRUE;
1024                         }
1025
1026                         pupdate_time();
1027
1028                         if (ps.picture && ps.next) {
1029                                 /* always at least set one step */
1030                                 while (ps.picture) {
1031                                         if (ps.next < 0) {
1032                                                 ps.picture = ps.picture->prev;
1033                                         }
1034                                         else {
1035                                                 ps.picture = ps.picture->next;
1036                                         }
1037
1038                                         if (ps.once && ps.picture != NULL) {
1039                                                 if (ps.picture->next == NULL) {
1040                                                         ps.wait2 = TRUE;
1041                                                 }
1042                                                 else if (ps.picture->prev == NULL) {
1043                                                         ps.wait2 = TRUE;
1044                                                 }
1045                                         }
1046
1047                                         if (ps.wait2 || ptottime < swaptime || ps.turbo || ps.noskip) break;
1048                                         ptottime -= swaptime;
1049                                 }
1050                                 if (ps.picture == NULL && ps.sstep) {
1051                                         if (ps.next < 0) {
1052                                                 ps.picture = picsbase.last;
1053                                         }
1054                                         else if (ps.next > 0) {
1055                                                 ps.picture = picsbase.first;
1056                                         }
1057                                 }
1058                         }
1059                         if (ps.go == FALSE) {
1060                                 break;
1061                         }
1062                 }
1063         }
1064         ps.picture = picsbase.first;
1065         anim = NULL;
1066         while (ps.picture) {
1067                 if (ps.picture && ps.picture->anim && (anim != ps.picture->anim)) {
1068                         // to prevent divx crashes
1069                         anim = ps.picture->anim;
1070                         IMB_close_anim(anim);
1071                 }
1072
1073                 if (ps.picture->ibuf) {
1074                         IMB_freeImBuf(ps.picture->ibuf);
1075                 }
1076                 if (ps.picture->mem) {
1077                         MEM_freeN(ps.picture->mem);
1078                 }
1079
1080                 ps.picture = ps.picture->next;
1081         }
1082
1083         /* cleanup */
1084 #ifndef USE_IMB_CACHE
1085         if (ibuf) IMB_freeImBuf(ibuf);
1086 #endif
1087
1088         BLI_freelistN(&picsbase);
1089 #if 0 // XXX25
1090         free_blender();
1091 #else
1092         /* we still miss freeing a lot!,
1093          * but many areas could skip initialization too for anim play */
1094         IMB_exit();
1095         BKE_images_exit();
1096         BLF_exit();
1097 #endif
1098         GHOST_DisposeWindow(g_WS.ghost_system, g_WS.ghost_window);
1099
1100         totblock = MEM_get_memory_blocks_in_use();
1101         if (totblock != 0) {
1102                 /* prints many bAKey, bArgument's which are tricky to fix */
1103 #if 0
1104                 printf("Error Totblock: %d\n", totblock);
1105                 MEM_printmemlist();
1106 #endif
1107         }
1108 }