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