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