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