]> git.sesse.net Git - vlc/blob - modules/video_filter/puzzle.c
Remove _GNU_SOURCE and string.h too
[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 #include <vlc/vlc.h>
29 #include <vlc_vout.h>
30
31 #include <math.h>
32
33 #include "filter_common.h"
34 #include "vlc_image.h"
35 #include "vlc_input.h"
36 #include "vlc_playlist.h"
37
38 /*****************************************************************************
39  * Local prototypes
40  *****************************************************************************/
41 static int  Create    ( vlc_object_t * );
42 static void Destroy   ( vlc_object_t * );
43
44 static int  Init      ( vout_thread_t * );
45 static void End       ( vout_thread_t * );
46 static void Render    ( vout_thread_t *, picture_t * );
47
48 static int  SendEvents   ( vlc_object_t *, char const *,
49                            vlc_value_t, vlc_value_t, void * );
50 static int  MouseEvent   ( vlc_object_t *, char const *,
51                            vlc_value_t, vlc_value_t, void * );
52
53 static int PuzzleCallback( vlc_object_t *, char const *,
54                            vlc_value_t, vlc_value_t, void * );
55
56 /*****************************************************************************
57  * Module descriptor
58  *****************************************************************************/
59
60 #define ROWS_TEXT N_("Number of puzzle rows")
61 #define ROWS_LONGTEXT N_("Number of puzzle rows")
62 #define COLS_TEXT N_("Number of puzzle columns")
63 #define COLS_LONGTEXT N_("Number of puzzle columns")
64 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
65 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
66
67 #define CFG_PREFIX "puzzle-"
68
69 vlc_module_begin();
70     set_description( _("Puzzle interactive game video filter") );
71     set_shortname( _( "Puzzle" ));
72     set_capability( "video filter", 0 );
73     set_category( CAT_VIDEO );
74     set_subcategory( SUBCAT_VIDEO_VFILTER );
75
76     add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
77                             ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE );
78     add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
79                             COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
80     add_bool( CFG_PREFIX "black-slot", 0, NULL,
81               BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_FALSE );
82
83     set_callbacks( Create, Destroy );
84 vlc_module_end();
85
86 static const char *ppsz_filter_options[] = {
87     "rows", "cols", "black-slot", NULL
88 };
89
90 /*****************************************************************************
91  * vout_sys_t: Magnify video output method descriptor
92  *****************************************************************************/
93 struct vout_sys_t
94 {
95     vout_thread_t *p_vout;
96
97     image_handler_t *p_image;
98
99     int i_cols;
100     int i_rows;
101     int *pi_order;
102     int i_selected;
103     vlc_bool_t b_finished;
104
105     vlc_bool_t b_blackslot;
106 };
107
108 /*****************************************************************************
109  * Control: control facility for the vout (forwards to child vout)
110  *****************************************************************************/
111 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
112 {
113     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
114 }
115
116 /*****************************************************************************
117  * Misc stuff...
118  *****************************************************************************/
119 static vlc_bool_t finished( vout_sys_t *p_sys )
120 {
121     int i;
122     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
123     {
124         if( i != p_sys->pi_order[i] ) return VLC_FALSE;
125     }
126     return VLC_TRUE;
127 }
128 static vlc_bool_t is_valid( vout_sys_t *p_sys )
129 {
130     int i, j, d=0;
131     if( p_sys->b_blackslot == VLC_FALSE ) return VLC_TRUE;
132     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
133     {
134         if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
135         {
136             d += i / p_sys->i_cols + 1;
137             continue;
138         }
139         for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
140         {
141             if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
142                 continue;
143             if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
144         }
145     }
146     if( d%2!=0 ) return VLC_FALSE;
147     else return VLC_TRUE;
148 }
149 static void shuffle( vout_sys_t *p_sys )
150 {
151     int i, c;
152     free( p_sys->pi_order );
153     p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
154     do
155     {
156         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
157         {
158             p_sys->pi_order[i] = -1;
159         }
160         i = 0;
161         for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
162         {
163             i = rand()%( p_sys->i_cols * p_sys->i_rows );
164             if( p_sys->pi_order[i] == -1 )
165             {
166                 p_sys->pi_order[i] = c;
167                 c++;
168             }
169         }
170         p_sys->b_finished = finished( p_sys );
171     } while(    p_sys->b_finished == VLC_TRUE
172              || is_valid( p_sys ) == VLC_FALSE );
173
174     if( p_sys->b_blackslot == VLC_TRUE )
175     {
176         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
177         {
178             if( p_sys->pi_order[i] ==
179                 p_sys->i_cols * p_sys->i_rows - 1 )
180             {
181                 p_sys->i_selected = i;
182                 break;
183             }
184         }
185     }
186     else
187     {
188         p_sys->i_selected = -1;
189     }
190 }
191
192 /*****************************************************************************
193  * Create: allocates Magnify video thread output method
194  *****************************************************************************/
195 static int Create( vlc_object_t *p_this )
196 {
197     vout_thread_t *p_vout = (vout_thread_t *)p_this;
198
199     /* Allocate structure */
200     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
201     if( p_vout->p_sys == NULL )
202     {
203         msg_Err( p_vout, "out of memory" );
204         return VLC_ENOMEM;
205     }
206
207     p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
208
209     config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
210                        p_vout->p_cfg );
211
212     p_vout->p_sys->i_rows =
213         var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "rows" );
214     p_vout->p_sys->i_cols =
215         var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "cols" );
216     p_vout->p_sys->b_blackslot =
217         var_CreateGetBoolCommand( p_vout, CFG_PREFIX "black-slot" );
218     var_AddCallback( p_vout, CFG_PREFIX "rows",
219                      PuzzleCallback, p_vout->p_sys );
220     var_AddCallback( p_vout, CFG_PREFIX "cols",
221                      PuzzleCallback, p_vout->p_sys );
222     var_AddCallback( p_vout, CFG_PREFIX "black-slot",
223                      PuzzleCallback, p_vout->p_sys );
224
225     p_vout->p_sys->pi_order = NULL;
226     shuffle( p_vout->p_sys );
227
228     p_vout->pf_init = Init;
229     p_vout->pf_end = End;
230     p_vout->pf_manage = NULL;
231     p_vout->pf_render = Render;
232     p_vout->pf_display = NULL;
233     p_vout->pf_control = Control;
234
235     return VLC_SUCCESS;
236 }
237
238 /*****************************************************************************
239  * Init: initialize Magnify video thread output method
240  *****************************************************************************/
241 static int Init( vout_thread_t *p_vout )
242 {
243     int i_index;
244     picture_t *p_pic;
245     video_format_t fmt;
246     memset( &fmt, 0, sizeof( video_format_t ) );
247
248     I_OUTPUTPICTURES = 0;
249
250     /* Initialize the output structure */
251     p_vout->output.i_chroma = p_vout->render.i_chroma;
252     p_vout->output.i_width  = p_vout->render.i_width;
253     p_vout->output.i_height = p_vout->render.i_height;
254     p_vout->output.i_aspect = p_vout->render.i_aspect;
255
256     p_vout->fmt_out = p_vout->fmt_in;
257     fmt = p_vout->fmt_out;
258
259     /* Try to open the real video output */
260     msg_Dbg( p_vout, "spawning the real video output" );
261
262     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
263
264     /* Everything failed */
265     if( p_vout->p_sys->p_vout == NULL )
266     {
267         msg_Err( p_vout, "cannot open vout, aborting" );
268         return VLC_EGENERIC;
269     }
270
271     var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
272     var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
273     var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
274                      MouseEvent, p_vout);
275
276     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
277     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
278     ADD_PARENT_CALLBACKS( SendEventsToChild );
279
280     return VLC_SUCCESS;
281 }
282
283 /*****************************************************************************
284  * End: terminate Magnify video thread output method
285  *****************************************************************************/
286 static void End( vout_thread_t *p_vout )
287 {
288     int i_index;
289
290     /* Free the fake output buffers we allocated */
291     for( i_index = I_OUTPUTPICTURES ; i_index ; )
292     {
293         i_index--;
294         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
295     }
296
297     var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
298     var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
299     var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
300 }
301
302 #define SHUFFLE_WIDTH 81
303 #define SHUFFLE_HEIGHT 13
304 static const char *shuffle_button[] =
305 {
306 ".................................................................................",
307 "..............  ............................   ........   ......  ...............",
308 "..............  ...........................  .........  ........  ...............",
309 "..............  ...........................  .........  ........  ...............",
310 "..     .......  .    .......  ....  ......     ......     ......  ........    ...",
311 ".  .... ......   ...  ......  ....  .......  .........  ........  .......  ..  ..",
312 ".  ...........  ....  ......  ....  .......  .........  ........  ......  ....  .",
313 ".      .......  ....  ......  ....  .......  .........  ........  ......        .",
314 "..      ......  ....  ......  ....  .......  .........  ........  ......  .......",
315 "......  ......  ....  ......  ....  .......  .........  ........  ......  .......",
316 ". ....  ......  ....  ......  ...   .......  .........  ........  .......  .... .",
317 "..     .......  ....  .......    .  .......  .........  ........  ........     ..",
318 "................................................................................."};
319
320
321 /*****************************************************************************
322  * Destroy: destroy Magnify video thread output method
323  *****************************************************************************/
324 static void Destroy( vlc_object_t *p_this )
325 {
326     vout_thread_t *p_vout = (vout_thread_t *)p_this;
327
328     if( p_vout->p_sys->p_vout )
329     {
330         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
331         vlc_object_detach( p_vout->p_sys->p_vout );
332         vout_Destroy( p_vout->p_sys->p_vout );
333     }
334
335     image_HandlerDelete( p_vout->p_sys->p_image );
336     free( p_vout->p_sys->pi_order );
337
338     DEL_PARENT_CALLBACKS( SendEventsToChild );
339
340     free( p_vout->p_sys );
341 }
342
343 /*****************************************************************************
344  * Render: displays previously rendered output
345  *****************************************************************************/
346 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
347 {
348     picture_t *p_outpic;
349
350     //video_format_t fmt_out;
351     // memset( &fmt_out, 0, sizeof(video_format_t) );
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 }