]> git.sesse.net Git - vlc/blob - modules/video_filter/puzzle.c
Remove unnedeeded msg_Error.
[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     /* Free the fake output buffers we allocated */
293     for( i_index = I_OUTPUTPICTURES ; i_index ; )
294     {
295         i_index--;
296         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
297     }
298
299     var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
300     var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
301     var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
302 }
303
304 #define SHUFFLE_WIDTH 81
305 #define SHUFFLE_HEIGHT 13
306 static const char *shuffle_button[] =
307 {
308 ".................................................................................",
309 "..............  ............................   ........   ......  ...............",
310 "..............  ...........................  .........  ........  ...............",
311 "..............  ...........................  .........  ........  ...............",
312 "..     .......  .    .......  ....  ......     ......     ......  ........    ...",
313 ".  .... ......   ...  ......  ....  .......  .........  ........  .......  ..  ..",
314 ".  ...........  ....  ......  ....  .......  .........  ........  ......  ....  .",
315 ".      .......  ....  ......  ....  .......  .........  ........  ......        .",
316 "..      ......  ....  ......  ....  .......  .........  ........  ......  .......",
317 "......  ......  ....  ......  ....  .......  .........  ........  ......  .......",
318 ". ....  ......  ....  ......  ...   .......  .........  ........  .......  .... .",
319 "..     .......  ....  .......    .  .......  .........  ........  ........     ..",
320 "................................................................................."};
321
322
323 /*****************************************************************************
324  * Destroy: destroy Magnify video thread output method
325  *****************************************************************************/
326 static void Destroy( vlc_object_t *p_this )
327 {
328     vout_thread_t *p_vout = (vout_thread_t *)p_this;
329
330     if( p_vout->p_sys->p_vout )
331     {
332         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
333         vlc_object_detach( p_vout->p_sys->p_vout );
334         vlc_object_release( p_vout->p_sys->p_vout );
335     }
336
337     image_HandlerDelete( p_vout->p_sys->p_image );
338     free( p_vout->p_sys->pi_order );
339
340     DEL_PARENT_CALLBACKS( SendEventsToChild );
341
342     free( p_vout->p_sys );
343 }
344
345 /*****************************************************************************
346  * Render: displays previously rendered output
347  *****************************************************************************/
348 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
349 {
350     picture_t *p_outpic;
351
352     //video_format_t fmt_out;
353     // memset( &fmt_out, 0, sizeof(video_format_t) );
354     //picture_t *p_converted;
355
356     int i_plane;
357
358     int i_rows = p_vout->p_sys->i_rows;
359     int i_cols = p_vout->p_sys->i_cols;
360
361     /* This is a new frame. Get a structure from the video_output. */
362     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
363               == NULL )
364     {
365         if( p_vout->b_die || p_vout->b_error )
366         {
367             return;
368         }
369         msleep( VOUT_OUTMEM_SLEEP );
370     }
371
372     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
373
374     for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
375     {
376         plane_t *p_in = p_pic->p+i_plane;
377         plane_t *p_out = p_outpic->p+i_plane;
378         int i_pitch = p_in->i_pitch;
379         int i;
380
381         for( i = 0; i < i_cols * i_rows; i++ )
382         {
383             int i_col = i % i_cols;
384             int i_row = i / i_cols;
385             int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
386             int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
387             int i_last_row = i_row + 1;
388             i_orow *= p_in->i_lines / i_rows;
389             i_row *= p_in->i_lines / i_rows;
390             i_last_row *= p_in->i_lines / i_rows;
391
392             if( p_vout->p_sys->b_blackslot == true
393                 && p_vout->p_sys->b_finished == false
394                 && i == p_vout->p_sys->i_selected )
395             {
396                 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
397                 for( ; i_row < i_last_row; i_row++, i_orow++ )
398                 {
399                     vlc_memset( p_out->p_pixels + i_row * i_pitch
400                                                + i_col * i_pitch / i_cols,
401                                color, i_pitch / i_cols );
402                 }
403             }
404             else
405             {
406                 for( ; i_row < i_last_row; i_row++, i_orow++ )
407                 {
408                     vlc_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 == 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         vlc_memset( p_out->p_pixels + i_row * i_pitch
430                                    + i_col * i_pitch / i_cols,
431                    0xff, i_pitch / i_cols );
432         for( ; i_row < i_last_row; i_row++ )
433         {
434             p_out->p_pixels[   i_row * i_pitch
435                              + i_col * i_pitch / i_cols ] = 0xff;
436             p_out->p_pixels[ i_row * i_pitch
437                              + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
438         }
439         i_row--;
440         vlc_memset( p_out->p_pixels + i_row * i_pitch
441                                    + i_col * i_pitch / i_cols,
442                    0xff, i_pitch / i_cols );
443     }
444
445     if( p_vout->p_sys->b_finished == true )
446     {
447         int i, j;
448         plane_t *p_out = p_outpic->p+Y_PLANE;
449         int i_pitch = p_out->i_pitch;
450         for( i = 0; i < SHUFFLE_HEIGHT; i++ )
451         {
452             for( j = 0; j < SHUFFLE_WIDTH; j++ )
453             {
454                 if( shuffle_button[i][j] == '.' )
455                    p_out->p_pixels[ i * i_pitch + j ] = 0xff;
456             }
457         }
458     }
459
460     vout_DisplayPicture( p_vout->p_sys->p_vout, p_outpic );
461 }
462
463 /*****************************************************************************
464  * SendEvents: forward mouse and keyboard events to the parent p_vout
465  *****************************************************************************/
466 static int SendEvents( vlc_object_t *p_this, char const *psz_var,
467                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
468 {
469     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
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     VLC_UNUSED(p_data); VLC_UNUSED(oldval);
483     vout_thread_t *p_vout = (vout_thread_t *)p_this;
484     var_Set( p_vout->p_sys->p_vout, psz_var, newval );
485     return VLC_SUCCESS;
486 }
487
488 /*****************************************************************************
489  * MouseEvent: callback for mouse events
490  *****************************************************************************/
491 static int MouseEvent( vlc_object_t *p_this, char const *psz_var,
492                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
493 {
494     VLC_UNUSED(p_this); VLC_UNUSED(oldval); VLC_UNUSED(newval);
495     vout_thread_t *p_vout = (vout_thread_t*)p_data;
496     int i_x, i_y;
497     int i_v;
498
499 #define MOUSE_DOWN    1
500 #define MOUSE_CLICKED 2
501 #define MOUSE_MOVE_X  4
502 #define MOUSE_MOVE_Y  8
503 #define MOUSE_MOVE    12
504     uint8_t mouse= 0;
505
506     int v_h = p_vout->output.i_height;
507     int v_w = p_vout->output.i_width;
508     int i_pos;
509
510     if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
511     if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
512     if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
513
514     i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
515     if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
516     i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
517     i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
518
519     if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
520         return VLC_SUCCESS;
521
522     if( mouse & MOUSE_CLICKED )
523     {
524         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;
525         if( p_vout->p_sys->b_finished == true
526             && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
527         {
528             shuffle( p_vout->p_sys );
529         }
530         else if( p_vout->p_sys->i_selected == -1 )
531         {
532             p_vout->p_sys->i_selected = i_pos;
533         }
534         else if( p_vout->p_sys->i_selected == i_pos
535                  && p_vout->p_sys->b_blackslot == false )
536         {
537             p_vout->p_sys->i_selected = -1;
538         }
539         else if(    ( p_vout->p_sys->i_selected == i_pos + 1
540                       && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
541                  || ( p_vout->p_sys->i_selected == i_pos - 1
542                       && i_pos % p_vout->p_sys->i_cols != 0 )
543                  || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
544                  || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
545         {
546             int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
547             p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
548                 p_vout->p_sys->pi_order[ i_pos ];
549             p_vout->p_sys->pi_order[ i_pos ] = a;
550             if( p_vout->p_sys->b_blackslot == true )
551                 p_vout->p_sys->i_selected = i_pos;
552             else
553                 p_vout->p_sys->i_selected = -1;
554
555             p_vout->p_sys->b_finished = finished( p_vout->p_sys );
556         }
557     }
558     return VLC_SUCCESS;
559 }
560
561 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
562                            vlc_value_t oldval, vlc_value_t newval,
563                            void *p_data )
564 {
565     VLC_UNUSED(p_this); VLC_UNUSED(oldval);
566     vout_sys_t *p_sys = (vout_sys_t *)p_data;
567     if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
568     {
569         p_sys->i_rows = __MAX( 1, newval.i_int );
570     }
571     else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
572     {
573         p_sys->i_cols = __MAX( 1, newval.i_int );
574     }
575     else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
576     {
577         p_sys->b_blackslot = newval.b_bool;
578     }
579     shuffle( p_sys );
580     return VLC_SUCCESS;
581 }