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