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