]> git.sesse.net Git - vlc/blob - modules/video_filter/puzzle.c
Make it possible to change puzzle parmaeters after filter launch.
[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 = {0};
248
249     I_OUTPUTPICTURES = 0;
250
251     /* Initialize the output structure */
252     p_vout->output.i_chroma = p_vout->render.i_chroma;
253     p_vout->output.i_width  = p_vout->render.i_width;
254     p_vout->output.i_height = p_vout->render.i_height;
255     p_vout->output.i_aspect = p_vout->render.i_aspect;
256
257     p_vout->fmt_out = p_vout->fmt_in;
258     fmt = p_vout->fmt_out;
259
260     /* Try to open the real video output */
261     msg_Dbg( p_vout, "spawning the real video output" );
262
263     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
264
265     /* Everything failed */
266     if( p_vout->p_sys->p_vout == NULL )
267     {
268         msg_Err( p_vout, "cannot open vout, aborting" );
269         return VLC_EGENERIC;
270     }
271
272     var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
273     var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
274     var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
275                      MouseEvent, p_vout);
276
277     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
278     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
279     ADD_PARENT_CALLBACKS( SendEventsToChild );
280
281     return VLC_SUCCESS;
282 }
283
284 /*****************************************************************************
285  * End: terminate Magnify video thread output method
286  *****************************************************************************/
287 static void End( vout_thread_t *p_vout )
288 {
289     int i_index;
290
291     /* Free the fake output buffers we allocated */
292     for( i_index = I_OUTPUTPICTURES ; i_index ; )
293     {
294         i_index--;
295         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
296     }
297
298     var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
299     var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
300     var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
301 }
302
303 #define SHUFFLE_WIDTH 81
304 #define SHUFFLE_HEIGHT 13
305 static const char *shuffle_button[] =
306 {
307 ".................................................................................",
308 "..............  ............................   ........   ......  ...............",
309 "..............  ...........................  .........  ........  ...............",
310 "..............  ...........................  .........  ........  ...............",
311 "..     .......  .    .......  ....  ......     ......     ......  ........    ...",
312 ".  .... ......   ...  ......  ....  .......  .........  ........  .......  ..  ..",
313 ".  ...........  ....  ......  ....  .......  .........  ........  ......  ....  .",
314 ".      .......  ....  ......  ....  .......  .........  ........  ......        .",
315 "..      ......  ....  ......  ....  .......  .........  ........  ......  .......",
316 "......  ......  ....  ......  ....  .......  .........  ........  ......  .......",
317 ". ....  ......  ....  ......  ...   .......  .........  ........  .......  .... .",
318 "..     .......  ....  .......    .  .......  .........  ........  ........     ..",
319 "................................................................................."};
320
321
322 /*****************************************************************************
323  * Destroy: destroy Magnify video thread output method
324  *****************************************************************************/
325 static void Destroy( vlc_object_t *p_this )
326 {
327     vout_thread_t *p_vout = (vout_thread_t *)p_this;
328
329     if( p_vout->p_sys->p_vout )
330     {
331         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
332         vlc_object_detach( p_vout->p_sys->p_vout );
333         vout_Destroy( p_vout->p_sys->p_vout );
334     }
335
336     image_HandlerDelete( p_vout->p_sys->p_image );
337     free( p_vout->p_sys->pi_order );
338
339     DEL_PARENT_CALLBACKS( SendEventsToChild );
340
341     free( p_vout->p_sys );
342 }
343
344 /*****************************************************************************
345  * Render: displays previously rendered output
346  *****************************************************************************/
347 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
348 {
349     picture_t *p_outpic;
350
351     //video_format_t fmt_out = {0};
352     //picture_t *p_converted;
353
354     int i_plane;
355
356     int i_rows = p_vout->p_sys->i_rows;
357     int i_cols = p_vout->p_sys->i_cols;
358
359     /* This is a new frame. Get a structure from the video_output. */
360     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
361               == NULL )
362     {
363         if( p_vout->b_die || p_vout->b_error )
364         {
365             return;
366         }
367         msleep( VOUT_OUTMEM_SLEEP );
368     }
369
370     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
371
372     for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
373     {
374         plane_t *p_in = p_pic->p+i_plane;
375         plane_t *p_out = p_outpic->p+i_plane;
376         int i_pitch = p_in->i_pitch;
377         int i;
378
379         for( i = 0; i < i_cols * i_rows; i++ )
380         {
381             int i_col = i % i_cols;
382             int i_row = i / i_cols;
383             int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
384             int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
385             int i_last_row = i_row + 1;
386             i_orow *= p_in->i_lines / i_rows;
387             i_row *= p_in->i_lines / i_rows;
388             i_last_row *= p_in->i_lines / i_rows;
389
390             if( p_vout->p_sys->b_blackslot == VLC_TRUE
391                 && p_vout->p_sys->b_finished == VLC_FALSE
392                 && i == p_vout->p_sys->i_selected )
393             {
394                 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
395                 for( ; i_row < i_last_row; i_row++, i_orow++ )
396                 {
397                     p_vout->p_libvlc->
398                     pf_memset( p_out->p_pixels + i_row * i_pitch
399                                                + i_col * i_pitch / i_cols,
400                                color, i_pitch / i_cols );
401                 }
402             }
403             else
404             {
405                 for( ; i_row < i_last_row; i_row++, i_orow++ )
406                 {
407                     p_vout->p_libvlc->
408                     pf_memcpy( p_out->p_pixels + i_row * i_pitch
409                                                + i_col * i_pitch / i_cols,
410                                p_in->p_pixels + i_orow * i_pitch
411                                               + i_ocol * i_pitch / i_cols,
412                                i_pitch / i_cols );
413                 }
414             }
415         }
416     }
417
418     if(    p_vout->p_sys->i_selected != -1
419         && p_vout->p_sys->b_blackslot == VLC_FALSE )
420     {
421         plane_t *p_in = p_pic->p+Y_PLANE;
422         plane_t *p_out = p_outpic->p+Y_PLANE;
423         int i_pitch = p_in->i_pitch;
424         int i_col = p_vout->p_sys->i_selected % i_cols;
425         int i_row = p_vout->p_sys->i_selected / i_cols;
426         int i_last_row = i_row + 1;
427         i_row *= p_in->i_lines / i_rows;
428         i_last_row *= p_in->i_lines / i_rows;
429         p_vout->p_libvlc->
430         pf_memset( p_out->p_pixels + i_row * i_pitch
431                                    + i_col * i_pitch / i_cols,
432                    0xff, i_pitch / i_cols );
433         for( ; i_row < i_last_row; i_row++ )
434         {
435             p_out->p_pixels[   i_row * i_pitch
436                              + i_col * i_pitch / i_cols ] = 0xff;
437             p_out->p_pixels[ i_row * i_pitch
438                              + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
439         }
440         i_row--;
441         p_vout->p_libvlc->
442         pf_memset( p_out->p_pixels + i_row * i_pitch
443                                    + i_col * i_pitch / i_cols,
444                    0xff, i_pitch / i_cols );
445     }
446
447     if( p_vout->p_sys->b_finished == VLC_TRUE )
448     {
449         int i, j;
450         plane_t *p_out = p_outpic->p+Y_PLANE;
451         int i_pitch = p_out->i_pitch;
452         for( i = 0; i < SHUFFLE_HEIGHT; i++ )
453         {
454             for( j = 0; j < SHUFFLE_WIDTH; j++ )
455             {
456                 if( shuffle_button[i][j] == '.' )
457                    p_out->p_pixels[ i * i_pitch + j ] = 0xff;
458             }
459         }
460     }
461
462     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
463 }
464
465 /*****************************************************************************
466  * SendEvents: forward mouse and keyboard events to the parent p_vout
467  *****************************************************************************/
468 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
469                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
470 {
471     var_Set( (vlc_object_t *)p_data, psz_var, newval );
472
473     return VLC_SUCCESS;
474 }
475
476 /*****************************************************************************
477  * SendEventsToChild: forward events to the child/children vout
478  *****************************************************************************/
479 static int SendEventsToChild( 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_this;
483     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
484     return VLC_SUCCESS;
485 }
486
487 /*****************************************************************************
488  * MouseEvent: callback for mouse events
489  *****************************************************************************/
490 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
491                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
492 {
493     vout_thread_t *p_vout = (vout_thread_t*)p_data;
494     int i_x, i_y;
495     int i_v;
496
497 #define MOUSE_DOWN    1
498 #define MOUSE_CLICKED 2
499 #define MOUSE_MOVE_X  4
500 #define MOUSE_MOVE_Y  8
501 #define MOUSE_MOVE    12
502     uint8_t mouse= 0;
503
504     int v_h = p_vout->output.i_height;
505     int v_w = p_vout->output.i_width;
506     int i_pos;
507
508     if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
509     if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
510     if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
511
512     i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
513     if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
514     i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
515     i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
516
517     if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
518         return VLC_SUCCESS;
519
520     if( mouse & MOUSE_CLICKED )
521     {
522         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;
523         if( p_vout->p_sys->b_finished == VLC_TRUE
524             && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
525         {
526             shuffle( p_vout->p_sys );
527         }
528         else if( p_vout->p_sys->i_selected == -1 )
529         {
530             p_vout->p_sys->i_selected = i_pos;
531         }
532         else if( p_vout->p_sys->i_selected == i_pos
533                  && p_vout->p_sys->b_blackslot == VLC_FALSE )
534         {
535             p_vout->p_sys->i_selected = -1;
536         }
537         else if(    ( p_vout->p_sys->i_selected == i_pos + 1
538                       && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
539                  || ( p_vout->p_sys->i_selected == i_pos - 1
540                       && i_pos % p_vout->p_sys->i_cols != 0 )
541                  || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
542                  || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
543         {
544             int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
545             p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
546                 p_vout->p_sys->pi_order[ i_pos ];
547             p_vout->p_sys->pi_order[ i_pos ] = a;
548             if( p_vout->p_sys->b_blackslot == VLC_TRUE )
549                 p_vout->p_sys->i_selected = i_pos;
550             else
551                 p_vout->p_sys->i_selected = -1;
552
553             p_vout->p_sys->b_finished = finished( p_vout->p_sys );
554         }
555     }
556     return VLC_SUCCESS;
557 }
558
559 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
560                            vlc_value_t oldval, vlc_value_t newval,
561                            void *p_data )
562 {
563     vout_sys_t *p_sys = (vout_sys_t *)p_data;
564     if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
565     {
566         p_sys->i_rows = __MAX( 1, newval.i_int );
567     }
568     else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
569     {
570         p_sys->i_cols = __MAX( 1, newval.i_int );
571     }
572     else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
573     {
574         p_sys->b_blackslot = newval.b_bool;
575     }
576     shuffle( p_sys );
577     return VLC_SUCCESS;
578 }