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