]> git.sesse.net Git - vlc/blob - modules/video_filter/puzzle.c
Remove stdlib.h
[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 <string.h>
28
29 #include <vlc/vlc.h>
30 #include <vlc_vout.h>
31
32 #include <math.h>
33
34 #include "filter_common.h"
35 #include "vlc_image.h"
36 #include "vlc_input.h"
37 #include "vlc_playlist.h"
38
39 /*****************************************************************************
40  * Local prototypes
41  *****************************************************************************/
42 static int  Create    ( vlc_object_t * );
43 static void Destroy   ( vlc_object_t * );
44
45 static int  Init      ( vout_thread_t * );
46 static void End       ( vout_thread_t * );
47 static void Render    ( vout_thread_t *, picture_t * );
48
49 static int  SendEvents   ( vlc_object_t *, char const *,
50                            vlc_value_t, vlc_value_t, void * );
51 static int  MouseEvent   ( vlc_object_t *, char const *,
52                            vlc_value_t, vlc_value_t, void * );
53
54 static int PuzzleCallback( vlc_object_t *, char const *,
55                            vlc_value_t, vlc_value_t, void * );
56
57 /*****************************************************************************
58  * Module descriptor
59  *****************************************************************************/
60
61 #define ROWS_TEXT N_("Number of puzzle rows")
62 #define ROWS_LONGTEXT N_("Number of puzzle rows")
63 #define COLS_TEXT N_("Number of puzzle columns")
64 #define COLS_LONGTEXT N_("Number of puzzle columns")
65 #define BLACKSLOT_TEXT N_("Make one tile a black slot")
66 #define BLACKSLOT_LONGTEXT N_("Make one slot black. Other tiles can only be swapped with the black slot.")
67
68 #define CFG_PREFIX "puzzle-"
69
70 vlc_module_begin();
71     set_description( _("Puzzle interactive game video filter") );
72     set_shortname( _( "Puzzle" ));
73     set_capability( "video filter", 0 );
74     set_category( CAT_VIDEO );
75     set_subcategory( SUBCAT_VIDEO_VFILTER );
76
77     add_integer_with_range( CFG_PREFIX "rows", 4, 1, 128, NULL,
78                             ROWS_TEXT, ROWS_LONGTEXT, VLC_FALSE );
79     add_integer_with_range( CFG_PREFIX "cols", 4, 1, 128, NULL,
80                             COLS_TEXT, COLS_LONGTEXT, VLC_FALSE );
81     add_bool( CFG_PREFIX "black-slot", 0, NULL,
82               BLACKSLOT_TEXT, BLACKSLOT_LONGTEXT, VLC_FALSE );
83
84     set_callbacks( Create, Destroy );
85 vlc_module_end();
86
87 static const char *ppsz_filter_options[] = {
88     "rows", "cols", "black-slot", NULL
89 };
90
91 /*****************************************************************************
92  * vout_sys_t: Magnify video output method descriptor
93  *****************************************************************************/
94 struct vout_sys_t
95 {
96     vout_thread_t *p_vout;
97
98     image_handler_t *p_image;
99
100     int i_cols;
101     int i_rows;
102     int *pi_order;
103     int i_selected;
104     vlc_bool_t b_finished;
105
106     vlc_bool_t b_blackslot;
107 };
108
109 /*****************************************************************************
110  * Control: control facility for the vout (forwards to child vout)
111  *****************************************************************************/
112 static int Control( vout_thread_t *p_vout, int i_query, va_list args )
113 {
114     return vout_vaControl( p_vout->p_sys->p_vout, i_query, args );
115 }
116
117 /*****************************************************************************
118  * Misc stuff...
119  *****************************************************************************/
120 static vlc_bool_t finished( vout_sys_t *p_sys )
121 {
122     int i;
123     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
124     {
125         if( i != p_sys->pi_order[i] ) return VLC_FALSE;
126     }
127     return VLC_TRUE;
128 }
129 static vlc_bool_t is_valid( vout_sys_t *p_sys )
130 {
131     int i, j, d=0;
132     if( p_sys->b_blackslot == VLC_FALSE ) return VLC_TRUE;
133     for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
134     {
135         if( p_sys->pi_order[i] == p_sys->i_cols * p_sys->i_rows - 1 )
136         {
137             d += i / p_sys->i_cols + 1;
138             continue;
139         }
140         for( j = i+1; j < p_sys->i_cols * p_sys->i_rows; j++ )
141         {
142             if( p_sys->pi_order[j] == p_sys->i_cols * p_sys->i_rows - 1 )
143                 continue;
144             if( p_sys->pi_order[i] > p_sys->pi_order[j] ) d++;
145         }
146     }
147     if( d%2!=0 ) return VLC_FALSE;
148     else return VLC_TRUE;
149 }
150 static void shuffle( vout_sys_t *p_sys )
151 {
152     int i, c;
153     free( p_sys->pi_order );
154     p_sys->pi_order = malloc( p_sys->i_cols * p_sys->i_rows * sizeof( int ) );
155     do
156     {
157         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
158         {
159             p_sys->pi_order[i] = -1;
160         }
161         i = 0;
162         for( c = 0; c < p_sys->i_cols * p_sys->i_rows; )
163         {
164             i = rand()%( p_sys->i_cols * p_sys->i_rows );
165             if( p_sys->pi_order[i] == -1 )
166             {
167                 p_sys->pi_order[i] = c;
168                 c++;
169             }
170         }
171         p_sys->b_finished = finished( p_sys );
172     } while(    p_sys->b_finished == VLC_TRUE
173              || is_valid( p_sys ) == VLC_FALSE );
174
175     if( p_sys->b_blackslot == VLC_TRUE )
176     {
177         for( i = 0; i < p_sys->i_cols * p_sys->i_rows; i++ )
178         {
179             if( p_sys->pi_order[i] ==
180                 p_sys->i_cols * p_sys->i_rows - 1 )
181             {
182                 p_sys->i_selected = i;
183                 break;
184             }
185         }
186     }
187     else
188     {
189         p_sys->i_selected = -1;
190     }
191 }
192
193 /*****************************************************************************
194  * Create: allocates Magnify video thread output method
195  *****************************************************************************/
196 static int Create( vlc_object_t *p_this )
197 {
198     vout_thread_t *p_vout = (vout_thread_t *)p_this;
199
200     /* Allocate structure */
201     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
202     if( p_vout->p_sys == NULL )
203     {
204         msg_Err( p_vout, "out of memory" );
205         return VLC_ENOMEM;
206     }
207
208     p_vout->p_sys->p_image = image_HandlerCreate( p_vout );
209
210     config_ChainParse( p_vout, CFG_PREFIX, ppsz_filter_options,
211                        p_vout->p_cfg );
212
213     p_vout->p_sys->i_rows =
214         var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "rows" );
215     p_vout->p_sys->i_cols =
216         var_CreateGetIntegerCommand( p_vout, CFG_PREFIX "cols" );
217     p_vout->p_sys->b_blackslot =
218         var_CreateGetBoolCommand( p_vout, CFG_PREFIX "black-slot" );
219     var_AddCallback( p_vout, CFG_PREFIX "rows",
220                      PuzzleCallback, p_vout->p_sys );
221     var_AddCallback( p_vout, CFG_PREFIX "cols",
222                      PuzzleCallback, p_vout->p_sys );
223     var_AddCallback( p_vout, CFG_PREFIX "black-slot",
224                      PuzzleCallback, p_vout->p_sys );
225
226     p_vout->p_sys->pi_order = NULL;
227     shuffle( p_vout->p_sys );
228
229     p_vout->pf_init = Init;
230     p_vout->pf_end = End;
231     p_vout->pf_manage = NULL;
232     p_vout->pf_render = Render;
233     p_vout->pf_display = NULL;
234     p_vout->pf_control = Control;
235
236     return VLC_SUCCESS;
237 }
238
239 /*****************************************************************************
240  * Init: initialize Magnify video thread output method
241  *****************************************************************************/
242 static int Init( vout_thread_t *p_vout )
243 {
244     int i_index;
245     picture_t *p_pic;
246     video_format_t fmt;
247     memset( &fmt, 0, sizeof( video_format_t ) );
248
249     I_OUTPUTPICTURES = 0;
250
251     /* Initialize the output structure */
252     p_vout->output.i_chroma = p_vout->render.i_chroma;
253     p_vout->output.i_width  = p_vout->render.i_width;
254     p_vout->output.i_height = p_vout->render.i_height;
255     p_vout->output.i_aspect = p_vout->render.i_aspect;
256
257     p_vout->fmt_out = p_vout->fmt_in;
258     fmt = p_vout->fmt_out;
259
260     /* Try to open the real video output */
261     msg_Dbg( p_vout, "spawning the real video output" );
262
263     p_vout->p_sys->p_vout = vout_Create( p_vout, &fmt );
264
265     /* Everything failed */
266     if( p_vout->p_sys->p_vout == NULL )
267     {
268         msg_Err( p_vout, "cannot open vout, aborting" );
269         return VLC_EGENERIC;
270     }
271
272     var_AddCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout );
273     var_AddCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout );
274     var_AddCallback( p_vout->p_sys->p_vout, "mouse-clicked",
275                      MouseEvent, p_vout);
276
277     ALLOCATE_DIRECTBUFFERS( VOUT_MAX_PICTURES );
278     ADD_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
279     ADD_PARENT_CALLBACKS( SendEventsToChild );
280
281     return VLC_SUCCESS;
282 }
283
284 /*****************************************************************************
285  * End: terminate Magnify video thread output method
286  *****************************************************************************/
287 static void End( vout_thread_t *p_vout )
288 {
289     int i_index;
290
291     /* Free the fake output buffers we allocated */
292     for( i_index = I_OUTPUTPICTURES ; i_index ; )
293     {
294         i_index--;
295         free( PP_OUTPUTPICTURE[ i_index ]->p_data_orig );
296     }
297
298     var_DelCallback( p_vout->p_sys->p_vout, "mouse-x", MouseEvent, p_vout);
299     var_DelCallback( p_vout->p_sys->p_vout, "mouse-y", MouseEvent, p_vout);
300     var_DelCallback( p_vout->p_sys->p_vout, "mouse-clicked", MouseEvent, p_vout);
301 }
302
303 #define SHUFFLE_WIDTH 81
304 #define SHUFFLE_HEIGHT 13
305 static const char *shuffle_button[] =
306 {
307 ".................................................................................",
308 "..............  ............................   ........   ......  ...............",
309 "..............  ...........................  .........  ........  ...............",
310 "..............  ...........................  .........  ........  ...............",
311 "..     .......  .    .......  ....  ......     ......     ......  ........    ...",
312 ".  .... ......   ...  ......  ....  .......  .........  ........  .......  ..  ..",
313 ".  ...........  ....  ......  ....  .......  .........  ........  ......  ....  .",
314 ".      .......  ....  ......  ....  .......  .........  ........  ......        .",
315 "..      ......  ....  ......  ....  .......  .........  ........  ......  .......",
316 "......  ......  ....  ......  ....  .......  .........  ........  ......  .......",
317 ". ....  ......  ....  ......  ...   .......  .........  ........  .......  .... .",
318 "..     .......  ....  .......    .  .......  .........  ........  ........     ..",
319 "................................................................................."};
320
321
322 /*****************************************************************************
323  * Destroy: destroy Magnify video thread output method
324  *****************************************************************************/
325 static void Destroy( vlc_object_t *p_this )
326 {
327     vout_thread_t *p_vout = (vout_thread_t *)p_this;
328
329     if( p_vout->p_sys->p_vout )
330     {
331         DEL_CALLBACKS( p_vout->p_sys->p_vout, SendEvents );
332         vlc_object_detach( p_vout->p_sys->p_vout );
333         vout_Destroy( p_vout->p_sys->p_vout );
334     }
335
336     image_HandlerDelete( p_vout->p_sys->p_image );
337     free( p_vout->p_sys->pi_order );
338
339     DEL_PARENT_CALLBACKS( SendEventsToChild );
340
341     free( p_vout->p_sys );
342 }
343
344 /*****************************************************************************
345  * Render: displays previously rendered output
346  *****************************************************************************/
347 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
348 {
349     picture_t *p_outpic;
350
351     //video_format_t fmt_out;
352     // memset( &fmt_out, 0, sizeof(video_format_t) );
353     //picture_t *p_converted;
354
355     int i_plane;
356
357     int i_rows = p_vout->p_sys->i_rows;
358     int i_cols = p_vout->p_sys->i_cols;
359
360     /* This is a new frame. Get a structure from the video_output. */
361     while( ( p_outpic = vout_CreatePicture( p_vout->p_sys->p_vout, 0, 0, 0 ) )
362               == NULL )
363     {
364         if( p_vout->b_die || p_vout->b_error )
365         {
366             return;
367         }
368         msleep( VOUT_OUTMEM_SLEEP );
369     }
370
371     vout_DatePicture( p_vout->p_sys->p_vout, p_outpic, p_pic->date );
372
373     for( i_plane = 0; i_plane < p_outpic->i_planes; i_plane++ )
374     {
375         plane_t *p_in = p_pic->p+i_plane;
376         plane_t *p_out = p_outpic->p+i_plane;
377         int i_pitch = p_in->i_pitch;
378         int i;
379
380         for( i = 0; i < i_cols * i_rows; i++ )
381         {
382             int i_col = i % i_cols;
383             int i_row = i / i_cols;
384             int i_ocol = p_vout->p_sys->pi_order[i] % i_cols;
385             int i_orow = p_vout->p_sys->pi_order[i] / i_cols;
386             int i_last_row = i_row + 1;
387             i_orow *= p_in->i_lines / i_rows;
388             i_row *= p_in->i_lines / i_rows;
389             i_last_row *= p_in->i_lines / i_rows;
390
391             if( p_vout->p_sys->b_blackslot == VLC_TRUE
392                 && p_vout->p_sys->b_finished == VLC_FALSE
393                 && i == p_vout->p_sys->i_selected )
394             {
395                 uint8_t color = ( i_plane == Y_PLANE ? 0x0 : 0x80 );
396                 for( ; i_row < i_last_row; i_row++, i_orow++ )
397                 {
398                     p_vout->p_libvlc->
399                     pf_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                     p_vout->p_libvlc->
409                     pf_memcpy( p_out->p_pixels + i_row * i_pitch
410                                                + i_col * i_pitch / i_cols,
411                                p_in->p_pixels + i_orow * i_pitch
412                                               + i_ocol * i_pitch / i_cols,
413                                i_pitch / i_cols );
414                 }
415             }
416         }
417     }
418
419     if(    p_vout->p_sys->i_selected != -1
420         && p_vout->p_sys->b_blackslot == VLC_FALSE )
421     {
422         plane_t *p_in = p_pic->p+Y_PLANE;
423         plane_t *p_out = p_outpic->p+Y_PLANE;
424         int i_pitch = p_in->i_pitch;
425         int i_col = p_vout->p_sys->i_selected % i_cols;
426         int i_row = p_vout->p_sys->i_selected / i_cols;
427         int i_last_row = i_row + 1;
428         i_row *= p_in->i_lines / i_rows;
429         i_last_row *= p_in->i_lines / i_rows;
430         p_vout->p_libvlc->
431         pf_memset( p_out->p_pixels + i_row * i_pitch
432                                    + i_col * i_pitch / i_cols,
433                    0xff, i_pitch / i_cols );
434         for( ; i_row < i_last_row; i_row++ )
435         {
436             p_out->p_pixels[   i_row * i_pitch
437                              + i_col * i_pitch / i_cols ] = 0xff;
438             p_out->p_pixels[ i_row * i_pitch
439                              + (i_col+1) * i_pitch / i_cols - 1 ] = 0xff;
440         }
441         i_row--;
442         p_vout->p_libvlc->
443         pf_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 == VLC_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     var_Set( (vlc_object_t *)p_data, psz_var, newval );
473
474     return VLC_SUCCESS;
475 }
476
477 /*****************************************************************************
478  * SendEventsToChild: forward events to the child/children vout
479  *****************************************************************************/
480 static int SendEventsToChild( vlc_object_t *p_this, char const *psz_var,
481                        vlc_value_t oldval, vlc_value_t newval, void *p_data )
482 {
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     vout_thread_t *p_vout = (vout_thread_t*)p_data;
495     int i_x, i_y;
496     int i_v;
497
498 #define MOUSE_DOWN    1
499 #define MOUSE_CLICKED 2
500 #define MOUSE_MOVE_X  4
501 #define MOUSE_MOVE_Y  8
502 #define MOUSE_MOVE    12
503     uint8_t mouse= 0;
504
505     int v_h = p_vout->output.i_height;
506     int v_w = p_vout->output.i_width;
507     int i_pos;
508
509     if( psz_var[6] == 'x' ) mouse |= MOUSE_MOVE_X;
510     if( psz_var[6] == 'y' ) mouse |= MOUSE_MOVE_Y;
511     if( psz_var[6] == 'c' ) mouse |= MOUSE_CLICKED;
512
513     i_v = var_GetInteger( p_vout->p_sys->p_vout, "mouse-button-down" );
514     if( i_v & 0x1 ) mouse |= MOUSE_DOWN;
515     i_y = var_GetInteger( p_vout->p_sys->p_vout, "mouse-y" );
516     i_x = var_GetInteger( p_vout->p_sys->p_vout, "mouse-x" );
517
518     if( i_y < 0 || i_x < 0 || i_y >= v_h || i_x >= v_w )
519         return VLC_SUCCESS;
520
521     if( mouse & MOUSE_CLICKED )
522     {
523         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;
524         if( p_vout->p_sys->b_finished == VLC_TRUE
525             && i_x < SHUFFLE_WIDTH && i_y < SHUFFLE_HEIGHT )
526         {
527             shuffle( p_vout->p_sys );
528         }
529         else if( p_vout->p_sys->i_selected == -1 )
530         {
531             p_vout->p_sys->i_selected = i_pos;
532         }
533         else if( p_vout->p_sys->i_selected == i_pos
534                  && p_vout->p_sys->b_blackslot == VLC_FALSE )
535         {
536             p_vout->p_sys->i_selected = -1;
537         }
538         else if(    ( p_vout->p_sys->i_selected == i_pos + 1
539                       && p_vout->p_sys->i_selected%p_vout->p_sys->i_cols != 0 )
540                  || ( p_vout->p_sys->i_selected == i_pos - 1
541                       && i_pos % p_vout->p_sys->i_cols != 0 )
542                  || p_vout->p_sys->i_selected == i_pos + p_vout->p_sys->i_cols
543                  || p_vout->p_sys->i_selected == i_pos - p_vout->p_sys->i_cols )
544         {
545             int a = p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ];
546             p_vout->p_sys->pi_order[ p_vout->p_sys->i_selected ] =
547                 p_vout->p_sys->pi_order[ i_pos ];
548             p_vout->p_sys->pi_order[ i_pos ] = a;
549             if( p_vout->p_sys->b_blackslot == VLC_TRUE )
550                 p_vout->p_sys->i_selected = i_pos;
551             else
552                 p_vout->p_sys->i_selected = -1;
553
554             p_vout->p_sys->b_finished = finished( p_vout->p_sys );
555         }
556     }
557     return VLC_SUCCESS;
558 }
559
560 static int PuzzleCallback( vlc_object_t *p_this, char const *psz_var,
561                            vlc_value_t oldval, vlc_value_t newval,
562                            void *p_data )
563 {
564     vout_sys_t *p_sys = (vout_sys_t *)p_data;
565     if( !strcmp( psz_var, CFG_PREFIX "rows" ) )
566     {
567         p_sys->i_rows = __MAX( 1, newval.i_int );
568     }
569     else if( !strcmp( psz_var, CFG_PREFIX "cols" ) )
570     {
571         p_sys->i_cols = __MAX( 1, newval.i_int );
572     }
573     else if( !strcmp( psz_var, CFG_PREFIX "black-slot" ) )
574     {
575         p_sys->b_blackslot = newval.b_bool;
576     }
577     shuffle( p_sys );
578     return VLC_SUCCESS;
579 }