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