video_filter: fix warnings
[vlc.git] / modules / video_filter / puzzle.c
1 /*****************************************************************************
2  * puzzle.c : Puzzle game
3  *****************************************************************************
4  * Copyright (C) 2005-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Antoine Cellerier <dionoea -at- videolan -dot- org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc/vlc.h>
33 #include <vlc_vout.h>
34
35 #include <math.h>
36
37 #include "filter_common.h"
38 #include "vlc_image.h"
39 #include "vlc_input.h"
40 #include "vlc_playlist.h"
41
42 /*****************************************************************************
43  * Local prototypes
44  *****************************************************************************/
45 static int  Create    ( vlc_object_t * );
46 static void Destroy   ( vlc_object_t * );
47
48 static int  Init      ( vout_thread_t * );
49 static void End       ( vout_thread_t * );
50 static void Render    ( vout_thread_t *, picture_t * );
51
52 static int  SendEvents   ( vlc_object_t *, char const *,
53                            vlc_value_t, vlc_value_t, void * );
54 static int  MouseEvent   ( vlc_object_t *, char const *,
55                            vlc_value_t, vlc_value_t, void * );
56
57 static int PuzzleCallback( vlc_object_t *, char const *,
58                            vlc_value_t, vlc_value_t, void * );
59
60 /*****************************************************************************
61  * Module descriptor
62  *****************************************************************************/
63
64 #define ROWS_TEXT N_("Number of puzzle rows")
65 #define ROWS_LONGTEXT N_("Number of puzzle rows")
66 #define COLS_TEXT N_("Number of puzzle columns")
67 #define COLS_LONGTEXT N_("Number of puzzle columns")
68 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
69 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
70
71 #define CFG_PREFIX "puzzle-"
72
73 vlc_module_begin();
74     set_description( _("Puzzle interactive game video filter") );
75     set_shortname( _( "Puzzle" ));
76     set_capability( "video filter", 0 );
77     set_category( CAT_VIDEO );
78     set_subcategory( SUBCAT_VIDEO_VFILTER );
79
80     add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
81                             ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE );
82     add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
83                             COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
84     add_bool( CFG_PREFIX "black-slot", 0, NULL,
85               BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_FALSE );
86
87     set_callbacks( Create, Destroy );
88 vlc_module_end();
89
90 static const char *ppsz_filter_options[] = {
91     "rows", "cols", "black-slot", NULL
92 };
93
94 /*****************************************************************************
95  * vout_sys_t: Magnify video output method descriptor
96  *****************************************************************************/
97 struct vout_sys_t
98 {
99     vout_thread_t *p_vout;
100
101     image_handler_t *p_image;
102
103     int i_cols;
104     int i_rows;
105     int *pi_order;
106     int i_selected;
107     vlc_bool_t b_finished;
108
109     vlc_bool_t b_blackslot;
110 };
111
112 /*****************************************************************************
113  * Control: control facility for the vout (forwards to child vout)
114  *****************************************************************************/
115 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
116 {
117     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
118 }
119
120 /*****************************************************************************
121  * Misc stuff...
122  *****************************************************************************/
123 static vlc_bool_t finished( vout_sys_t *p_sys )
124 {
125     int i;
126     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
127     {
128         if( i != p_sys->pi_order[i] ) return VLC_FALSE;
129     }
130     return VLC_TRUE;
131 }
132 static vlc_bool_t is_valid( vout_sys_t *p_sys )
133 {
134     int i, j, d=0;
135     if( p_sys->b_blackslot == VLC_FALSE ) return VLC_TRUE;
136     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
137     {
138         if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
139         {
140             d += i / p_sys->i_cols + 1;
141             continue;
142         }
143         for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
144         {
145             if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
146                 continue;
147             if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
148         }
149     }
150     if( d%2!=0 ) return VLC_FALSE;
151     else return VLC_TRUE;
152 }
153 static void shuffle( vout_sys_t *p_sys )
154 {
155     int i, c;
156     free( p_sys->pi_order );
157     p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
158     do
159     {
160         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
161         {
162             p_sys->pi_order[i] = -1;
163         }
164         i = 0;
165         for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
166         {
167             i = rand()%( p_sys->i_cols * p_sys->i_rows );
168             if( p_sys->pi_order[i] == -1 )
169             {
170                 p_sys->pi_order[i] = c;
171                 c++;
172             }
173         }
174         p_sys->b_finished = finished( p_sys );
175     } while(    p_sys->b_finished == VLC_TRUE
176              || is_valid( p_sys ) == VLC_FALSE );
177
178     if( p_sys->b_blackslot == VLC_TRUE )
179     {
180         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
181         {
182             if( p_sys->pi_order[i] ==
183                 p_sys->i_cols * p_sys->i_rows - 1 )
184             {
185                 p_sys->i_selected = i;
186                 break;
187             }
188         }
189     }
190     else
191     {
192         p_sys->i_selected = -1;
193     }
194 }
195
196 /*****************************************************************************
197  * Create: allocates Magnify video thread output method
198  *****************************************************************************/
199 static int Create( vlc_object_t *p_this )
200 {
201     vout_thread_t *p_vout = (vout_thread_t *)p_this;
202
203     /* Allocate structure */
204     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
205     if( p_vout->p_sys == NULL )
206     {
207         msg_Err( p_vout, "out of memory" );
208         return VLC_ENOMEM;
209     }
210
211     p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
212
213     config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
214                        p_vout->p_cfg );
215
216     p_vout->p_sys->i_rows =
217         var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "rows" );
218     p_vout->p_sys->i_cols =
219         var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "cols" );
220     p_vout->p_sys->b_blackslot =
221         var_CreateGetBoolCommand( p_vout, CFG_PREFIX "black-slot" );
222     var_AddCallback( p_vout, CFG_PREFIX "rows",
223                      PuzzleCallback, p_vout->p_sys );
224     var_AddCallback( p_vout, CFG_PREFIX "cols",
225                      PuzzleCallback, p_vout->p_sys );
226     var_AddCallback( p_vout, CFG_PREFIX "black-slot",
227                      PuzzleCallback, p_vout->p_sys );
228
229     p_vout->p_sys->pi_order = NULL;
230     shuffle( p_vout->p_sys );
231
232     p_vout->pf_init = Init;
233     p_vout->pf_end = End;
234     p_vout->pf_manage = NULL;
235     p_vout->pf_render = Render;
236     p_vout->pf_display = NULL;
237     p_vout->pf_control = Control;
238
239     return VLC_SUCCESS;
240 }
241
242 /*****************************************************************************
243  * Init: initialize Magnify video thread output method
244  *****************************************************************************/
245 static int Init( vout_thread_t *p_vout )
246 {
247     int i_index;
248     picture_t *p_pic;
249     video_format_t fmt;
250     memset( &fmt, 0, sizeof( video_format_t ) );
251
252     I_OUTPUTPICTURES = 0;
253
254     /* Initialize the output structure */
255     p_vout->output.i_chroma = p_vout->render.i_chroma;
256     p_vout->output.i_width  = p_vout->render.i_width;
257     p_vout->output.i_height = p_vout->render.i_height;
258     p_vout->output.i_aspect = p_vout->render.i_aspect;
259
260     p_vout->fmt_out = p_vout->fmt_in;
261     fmt = p_vout->fmt_out;
262
263     /* Try to open the real video output */
264     msg_Dbg( p_vout, "spawning the real video output" );
265
266     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
267
268     /* Everything failed */
269     if( p_vout->p_sys->p_vout == NULL )
270     {
271         msg_Err( p_vout, "cannot open vout, aborting" );
272         return VLC_EGENERIC;
273     }
274
275     var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
276     var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
277     var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
278                      MouseEvent, p_vout);
279
280     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
281     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
282     ADD_PARENT_CALLBACKS( SendEventsToChild );
283
284     return VLC_SUCCESS;
285 }
286
287 /*****************************************************************************
288  * End: terminate Magnify video thread output method
289  *****************************************************************************/
290 static void End( vout_thread_t *p_vout )
291 {
292     int i_index;
293
294     /* Free the fake output buffers we allocated */
295     for( i_index = I_OUTPUTPICTURES ; i_index ; )
296     {
297         i_index--;
298         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
299     }
300
301     var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
302     var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
303     var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
304 }
305
306 #define SHUFFLE_WIDTH 81
307 #define SHUFFLE_HEIGHT 13
308 static const char *shuffle_button[] =
309 {
310 ".................................................................................",
311 "..............  ............................   ........   ......  ...............",
312 "..............  ...........................  .........  ........  ...............",
313 "..............  ...........................  .........  ........  ...............",
314 "..     .......  .    .......  ....  ......     ......     ......  ........    ...",
315 ".  .... ......   ...  ......  ....  .......  .........  ........  .......  ..  ..",
316 ".  ...........  ....  ......  ....  .......  .........  ........  ......  ....  .",
317 ".      .......  ....  ......  ....  .......  .........  ........  ......        .",
318 "..      ......  ....  ......  ....  .......  .........  ........  ......  .......",
319 "......  ......  ....  ......  ....  .......  .........  ........  ......  .......",
320 ". ....  ......  ....  ......  ...   .......  .........  ........  .......  .... .",
321 "..     .......  ....  .......    .  .......  .........  ........  ........     ..",
322 "................................................................................."};
323
324
325 /*****************************************************************************
326  * Destroy: destroy Magnify video thread output method
327  *****************************************************************************/
328 static void Destroy( vlc_object_t *p_this )
329 {
330     vout_thread_t *p_vout = (vout_thread_t *)p_this;
331
332     if( p_vout->p_sys->p_vout )
333     {
334         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
335         vlc_object_detach( p_vout->p_sys->p_vout );
336         vout_Destroy( p_vout->p_sys->p_vout );
337     }
338
339     image_HandlerDelete( p_vout->p_sys->p_image );
340     free( p_vout->p_sys->pi_order );
341
342     DEL_PARENT_CALLBACKS( SendEventsToChild );
343
344     free( p_vout->p_sys );
345 }
346
347 /*****************************************************************************
348  * Render: displays previously rendered output
349  *****************************************************************************/
350 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
351 {
352     picture_t *p_outpic;
353
354     //video_format_t fmt_out;
355     // memset( &fmt_out, 0, sizeof(video_format_t) );
356     //picture_t *p_converted;
357
358     int i_plane;
359
360     int i_rows = p_vout->p_sys->i_rows;
361     int i_cols = p_vout->p_sys->i_cols;
362
363     /* This is a new frame. Get a structure from the video_output. */
364     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
365               == NULL )
366     {
367         if( p_vout->b_die || p_vout->b_error )
368         {
369             return;
370         }
371         msleep( VOUT_OUTMEM_SLEEP );
372     }
373
374     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
375
376     for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
377     {
378         plane_t *p_in = p_pic->p+i_plane;
379         plane_t *p_out = p_outpic->p+i_plane;
380         int i_pitch = p_in->i_pitch;
381         int i;
382
383         for( i = 0; i < i_cols * i_rows; i++ )
384         {
385             int i_col = i % i_cols;
386             int i_row = i / i_cols;
387             int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
388             int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
389             int i_last_row = i_row + 1;
390             i_orow *= p_in->i_lines / i_rows;
391             i_row *= p_in->i_lines / i_rows;
392             i_last_row *= p_in->i_lines / i_rows;
393
394             if( p_vout->p_sys->b_blackslot == VLC_TRUE
395                 && p_vout->p_sys->b_finished == VLC_FALSE
396                 && i == p_vout->p_sys->i_selected )
397             {
398                 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
399                 for( ; i_row < i_last_row; i_row++, i_orow++ )
400                 {
401                     p_vout->p_libvlc->
402                     pf_memset( p_out->p_pixels + i_row * i_pitch
403                                                + i_col * i_pitch / i_cols,
404                                color, i_pitch / i_cols );
405                 }
406             }
407             else
408             {
409                 for( ; i_row < i_last_row; i_row++, i_orow++ )
410                 {
411                     p_vout->p_libvlc->
412                     pf_memcpy( p_out->p_pixels + i_row * i_pitch
413                                                + i_col * i_pitch / i_cols,
414                                p_in->p_pixels + i_orow * i_pitch
415                                               + i_ocol * i_pitch / i_cols,
416                                i_pitch / i_cols );
417                 }
418             }
419         }
420     }
421
422     if(    p_vout->p_sys->i_selected != -1
423         && p_vout->p_sys->b_blackslot == VLC_FALSE )
424     {
425         plane_t *p_in = p_pic->p+Y_PLANE;
426         plane_t *p_out = p_outpic->p+Y_PLANE;
427         int i_pitch = p_in->i_pitch;
428         int i_col = p_vout->p_sys->i_selected % i_cols;
429         int i_row = p_vout->p_sys->i_selected / i_cols;
430         int i_last_row = i_row + 1;
431         i_row *= p_in->i_lines / i_rows;
432         i_last_row *= p_in->i_lines / i_rows;
433         p_vout->p_libvlc->
434         pf_memset( p_out->p_pixels + i_row * i_pitch
435                                    + i_col * i_pitch / i_cols,
436                    0xff, i_pitch / i_cols );
437         for( ; i_row < i_last_row; i_row++ )
438         {
439             p_out->p_pixels[   i_row * i_pitch
440                              + i_col * i_pitch / i_cols ] = 0xff;
441             p_out->p_pixels[ i_row * i_pitch
442                              + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
443         }
444         i_row--;
445         p_vout->p_libvlc->
446         pf_memset( p_out->p_pixels + i_row * i_pitch
447                                    + i_col * i_pitch / i_cols,
448                    0xff, i_pitch / i_cols );
449     }
450
451     if( p_vout->p_sys->b_finished == VLC_TRUE )
452     {
453         int i, j;
454         plane_t *p_out = p_outpic->p+Y_PLANE;
455         int i_pitch = p_out->i_pitch;
456         for( i = 0; i < SHUFFLE_HEIGHT; i++ )
457         {
458             for( j = 0; j < SHUFFLE_WIDTH; j++ )
459             {
460                 if( shuffle_button[i][j] == '.' )
461                    p_out->p_pixels[ i * i_pitch + j ] = 0xff;
462             }
463         }
464     }
465
466     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
467 }
468
469 /*****************************************************************************
470  * SendEvents: forward mouse and keyboard events to the parent p_vout
471  *****************************************************************************/
472 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
473                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
474 {
475     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
476
477     var_Set( (vlc_object_t *)p_data, psz_var, newval );
478
479     return VLC_SUCCESS;
480 }
481
482 /*****************************************************************************
483  * SendEventsToChild: forward events to the child/children vout
484  *****************************************************************************/
485 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
486                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
487 {
488     VLC_UNUSED(p_data); VLC_UNUSED(oldval);
489     vout_thread_t *p_vout = (vout_thread_t *)p_this;
490     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
491     return VLC_SUCCESS;
492 }
493
494 /*****************************************************************************
495  * MouseEvent: callback for mouse events
496  *****************************************************************************/
497 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
498                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
499 {
500     VLC_UNUSED(p_this); VLC_UNUSED(oldval); VLC_UNUSED(newval);
501     vout_thread_t *p_vout = (vout_thread_t*)p_data;
502     int i_x, i_y;
503     int i_v;
504
505 #define MOUSE_DOWN    1
506 #define MOUSE_CLICKED 2
507 #define MOUSE_MOVE_X  4
508 #define MOUSE_MOVE_Y  8
509 #define MOUSE_MOVE    12
510     uint8_t mouse= 0;
511
512     int v_h = p_vout->output.i_height;
513     int v_w = p_vout->output.i_width;
514     int i_pos;
515
516     if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
517     if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
518     if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
519
520     i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
521     if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
522     i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
523     i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
524
525     if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
526         return VLC_SUCCESS;
527
528     if( mouse & MOUSE_CLICKED )
529     {
530         i_pos = p_vout->p_sys->i_cols * ( ( p_vout->p_sys->i_rows * i_y ) / v_h ) + (p_vout->p_sys->i_cols * i_x ) / v_w;
531         if( p_vout->p_sys->b_finished == VLC_TRUE
532             && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
533         {
534             shuffle( p_vout->p_sys );
535         }
536         else if( p_vout->p_sys->i_selected == -1 )
537         {
538             p_vout->p_sys->i_selected = i_pos;
539         }
540         else if( p_vout->p_sys->i_selected == i_pos
541                  && p_vout->p_sys->b_blackslot == VLC_FALSE )
542         {
543             p_vout->p_sys->i_selected = -1;
544         }
545         else if(    ( p_vout->p_sys->i_selected == i_pos + 1
546                       && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
547                  || ( p_vout->p_sys->i_selected == i_pos - 1
548                       && i_pos % p_vout->p_sys->i_cols != 0 )
549                  || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
550                  || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
551         {
552             int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
553             p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
554                 p_vout->p_sys->pi_order[ i_pos ];
555             p_vout->p_sys->pi_order[ i_pos ] = a;
556             if( p_vout->p_sys->b_blackslot == VLC_TRUE )
557                 p_vout->p_sys->i_selected = i_pos;
558             else
559                 p_vout->p_sys->i_selected = -1;
560
561             p_vout->p_sys->b_finished = finished( p_vout->p_sys );
562         }
563     }
564     return VLC_SUCCESS;
565 }
566
567 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
568                            vlc_value_t oldval, vlc_value_t newval,
569                            void *p_data )
570 {
571     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
572     vout_sys_t *p_sys = (vout_sys_t *)p_data;
573     if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
574     {
575         p_sys->i_rows = __MAX( 1, newval.i_int );
576     }
577     else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
578     {
579         p_sys->i_cols = __MAX( 1, newval.i_int );
580     }
581     else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
582     {
583         p_sys->b_blackslot = newval.b_bool;
584     }
585     shuffle( p_sys );
586     return VLC_SUCCESS;
587 }