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