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