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