ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.37
Committed: 2004-01-12T15:29:25Z (20 years, 10 months ago) by cebix
Content type: text/plain
Branch: MAIN
CVS Tags: nigel-build-16, nigel-build-15
Changes since 1.36: +1 -1 lines
Log Message:
Happy New Year! :)

File Contents

# User Rev Content
1 gbeauche 1.1 /*
2     * video_vosf.h - Video/graphics emulation, video on SEGV signals support
3     *
4 cebix 1.37 * Basilisk II (C) 1997-2004 Christian Bauer
5 gbeauche 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #ifndef VIDEO_VOSF_H
22     #define VIDEO_VOSF_H
23    
24 gbeauche 1.34 // Note: this file must be #include'd only in video_x.cpp
25 gbeauche 1.1 #ifdef ENABLE_VOSF
26    
27 cebix 1.19 #include <fcntl.h>
28     #include <sys/mman.h>
29     #include "sigsegv.h"
30     #include "vm_alloc.h"
31    
32 gbeauche 1.33 // Glue for SheepShaver and BasiliskII
33     #if POWERPC_ROM
34     #define X11_MONITOR_INIT /* nothing */
35     #define VIDEO_DRV_INIT /* nothing */
36     #define VIDEO_DRV_WINDOW the_win
37     #define VIDEO_DRV_GC the_gc
38     #define VIDEO_DRV_IMAGE img
39     #define VIDEO_DRV_HAVE_SHM have_shm
40     #define VIDEO_MODE_INIT VideoInfo const & mode = VModes[cur_mode]
41     #define VIDEO_MODE_ROW_BYTES mode.viRowBytes
42     #define VIDEO_MODE_X mode.viXsize
43     #define VIDEO_MODE_Y mode.viYsize
44     #define VIDEO_MODE_DEPTH mode.viAppleMode
45     enum {
46     VIDEO_DEPTH_1BIT = APPLE_1_BIT,
47     VIDEO_DEPTH_2BIT = APPLE_2_BIT,
48     VIDEO_DEPTH_4BIT = APPLE_4_BIT,
49     VIDEO_DEPTH_8BIT = APPLE_8_BIT,
50     VIDEO_DEPTH_16BIT = APPLE_16_BIT,
51     VIDEO_DEPTH_32BIT = APPLE_32_BIT
52     };
53     #else
54     #define X11_MONITOR_INIT X11_monitor_desc &monitor
55     #define VIDEO_DRV_INIT driver_window *drv
56     #define VIDEO_DRV_WINDOW drv->w
57     #define VIDEO_DRV_GC drv->gc
58     #define VIDEO_DRV_IMAGE drv->img
59     #define VIDEO_DRV_HAVE_SHM drv->have_shm
60     #define VIDEO_MODE_INIT video_mode const & mode = drv->monitor.get_current_mode();
61     #define VIDEO_MODE_ROW_BYTES mode.bytes_per_row
62     #define VIDEO_MODE_X mode.x
63     #define VIDEO_MODE_Y mode.y
64     #define VIDEO_MODE_DEPTH (int)mode.depth
65     enum {
66     VIDEO_DEPTH_1BIT = VDEPTH_1BIT,
67     VIDEO_DEPTH_2BIT = VDEPTH_2BIT,
68     VIDEO_DEPTH_4BIT = VDEPTH_4BIT,
69     VIDEO_DEPTH_8BIT = VDEPTH_8BIT,
70     VIDEO_DEPTH_16BIT = VDEPTH_16BIT,
71     VIDEO_DEPTH_32BIT = VDEPTH_32BIT
72     };
73     #endif
74    
75 cebix 1.19 // Variables for Video on SEGV support
76 gbeauche 1.20 static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
77 cebix 1.19
78     struct ScreenPageInfo {
79     int top, bottom; // Mapping between this virtual page and Mac scanlines
80     };
81    
82     struct ScreenInfo {
83     uintptr memStart; // Start address aligned to page boundary
84     uint32 memLength; // Length of the memory addressed by the screen pages
85    
86 gbeauche 1.27 uintptr pageSize; // Size of a page
87 cebix 1.19 int pageBits; // Shift count to get the page number
88     uint32 pageCount; // Number of pages allocated to the screen
89    
90     bool dirty; // Flag: set if the frame buffer was touched
91     char * dirtyPages; // Table of flags set if page was altered
92     ScreenPageInfo * pageInfo; // Table of mappings page -> Mac scanlines
93     };
94    
95     static ScreenInfo mainBuffer;
96    
97     #define PFLAG_SET_VALUE 0x00
98     #define PFLAG_CLEAR_VALUE 0x01
99     #define PFLAG_SET_VALUE_4 0x00000000
100     #define PFLAG_CLEAR_VALUE_4 0x01010101
101     #define PFLAG_SET(page) mainBuffer.dirtyPages[page] = PFLAG_SET_VALUE
102     #define PFLAG_CLEAR(page) mainBuffer.dirtyPages[page] = PFLAG_CLEAR_VALUE
103     #define PFLAG_ISSET(page) (mainBuffer.dirtyPages[page] == PFLAG_SET_VALUE)
104     #define PFLAG_ISCLEAR(page) (mainBuffer.dirtyPages[page] != PFLAG_SET_VALUE)
105    
106     #ifdef UNALIGNED_PROFITABLE
107     # define PFLAG_ISSET_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_SET_VALUE_4)
108     # define PFLAG_ISCLEAR_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_CLEAR_VALUE_4)
109     #else
110     # define PFLAG_ISSET_4(page) \
111     PFLAG_ISSET(page ) && PFLAG_ISSET(page+1) \
112     && PFLAG_ISSET(page+2) && PFLAG_ISSET(page+3)
113     # define PFLAG_ISCLEAR_4(page) \
114     PFLAG_ISCLEAR(page ) && PFLAG_ISCLEAR(page+1) \
115     && PFLAG_ISCLEAR(page+2) && PFLAG_ISCLEAR(page+3)
116     #endif
117    
118     // Set the selected page range [ first_page, last_page [ into the SET state
119     #define PFLAG_SET_RANGE(first_page, last_page) \
120     memset(mainBuffer.dirtyPages + (first_page), PFLAG_SET_VALUE, \
121     (last_page) - (first_page))
122    
123     // Set the selected page range [ first_page, last_page [ into the CLEAR state
124     #define PFLAG_CLEAR_RANGE(first_page, last_page) \
125     memset(mainBuffer.dirtyPages + (first_page), PFLAG_CLEAR_VALUE, \
126     (last_page) - (first_page))
127    
128     #define PFLAG_SET_ALL do { \
129     PFLAG_SET_RANGE(0, mainBuffer.pageCount); \
130     mainBuffer.dirty = true; \
131     } while (0)
132    
133     #define PFLAG_CLEAR_ALL do { \
134     PFLAG_CLEAR_RANGE(0, mainBuffer.pageCount); \
135     mainBuffer.dirty = false; \
136     } while (0)
137    
138     // Set the following macro definition to 1 if your system
139     // provides a really fast strchr() implementation
140     //#define HAVE_FAST_STRCHR 0
141    
142     static inline int find_next_page_set(int page)
143     {
144     #if HAVE_FAST_STRCHR
145     char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_SET_VALUE);
146     return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
147     #else
148     while (PFLAG_ISCLEAR_4(page))
149     page += 4;
150     while (PFLAG_ISCLEAR(page))
151     page++;
152     return page;
153     #endif
154     }
155    
156     static inline int find_next_page_clear(int page)
157     {
158     #if HAVE_FAST_STRCHR
159     char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_CLEAR_VALUE);
160     return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
161     #else
162     while (PFLAG_ISSET_4(page))
163     page += 4;
164     while (PFLAG_ISSET(page))
165     page++;
166     return page;
167     #endif
168     }
169    
170 gbeauche 1.36 #ifdef HAVE_SPINLOCKS
171     static spinlock_t vosf_lock = SPIN_LOCK_UNLOCKED; // Mutex to protect frame buffer (dirtyPages in fact)
172     #define LOCK_VOSF spin_lock(&vosf_lock)
173     #define UNLOCK_VOSF spin_unlock(&vosf_lock)
174     #elif defined(HAVE_PTHREADS)
175 cebix 1.19 static pthread_mutex_t vosf_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer (dirtyPages in fact)
176     #define LOCK_VOSF pthread_mutex_lock(&vosf_lock);
177     #define UNLOCK_VOSF pthread_mutex_unlock(&vosf_lock);
178     #else
179     #define LOCK_VOSF
180     #define UNLOCK_VOSF
181     #endif
182    
183     static int log_base_2(uint32 x)
184     {
185     uint32 mask = 0x80000000;
186     int l = 31;
187     while (l >= 0 && (x & mask) == 0) {
188     mask >>= 1;
189     l--;
190     }
191     return l;
192     }
193    
194 gbeauche 1.20 // Extend size to page boundary
195     static uint32 page_extend(uint32 size)
196     {
197     const uint32 page_size = getpagesize();
198     const uint32 page_mask = page_size - 1;
199     return (size + page_mask) & ~page_mask;
200     }
201    
202 cebix 1.19
203     /*
204 gbeauche 1.27 * Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
205 cebix 1.19 */
206    
207 gbeauche 1.33 static bool video_vosf_init(X11_MONITOR_INIT)
208 cebix 1.19 {
209 gbeauche 1.33 VIDEO_MODE_INIT;
210 cebix 1.31
211 gbeauche 1.27 const uintptr page_size = getpagesize();
212     const uintptr page_mask = page_size - 1;
213    
214     // Round up frame buffer base to page boundary
215     mainBuffer.memStart = (((uintptr) the_buffer) + page_mask) & ~page_mask;
216    
217     // The frame buffer size shall already be aligned to page boundary (use page_extend)
218     mainBuffer.memLength = the_buffer_size;
219    
220     mainBuffer.pageSize = page_size;
221     mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
222     mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
223    
224     // The "2" more bytes requested are a safety net to insure the
225     // loops in the update routines will terminate.
226     // See "How can we deal with array overrun conditions ?" hereunder for further details.
227 gbeauche 1.29 mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
228     if (mainBuffer.dirtyPages == NULL)
229 gbeauche 1.27 return false;
230 cebix 1.19
231 gbeauche 1.27 PFLAG_CLEAR_ALL;
232     PFLAG_CLEAR(mainBuffer.pageCount);
233     PFLAG_SET(mainBuffer.pageCount+1);
234    
235     // Allocate and fill in pageInfo with start and end (inclusive) row in number of bytes
236 gbeauche 1.29 mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
237     if (mainBuffer.pageInfo == NULL)
238 gbeauche 1.27 return false;
239    
240     uint32 a = 0;
241 cebix 1.28 for (unsigned i = 0; i < mainBuffer.pageCount; i++) {
242 gbeauche 1.33 unsigned y1 = a / VIDEO_MODE_ROW_BYTES;
243     if (y1 >= VIDEO_MODE_Y)
244     y1 = VIDEO_MODE_Y - 1;
245    
246     unsigned y2 = (a + mainBuffer.pageSize) / VIDEO_MODE_ROW_BYTES;
247     if (y2 >= VIDEO_MODE_Y)
248     y2 = VIDEO_MODE_Y - 1;
249 gbeauche 1.27
250     mainBuffer.pageInfo[i].top = y1;
251     mainBuffer.pageInfo[i].bottom = y2;
252    
253     a += mainBuffer.pageSize;
254     if (a > mainBuffer.memLength)
255     a = mainBuffer.memLength;
256     }
257    
258     // We can now write-protect the frame buffer
259     if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
260     return false;
261    
262     // The frame buffer is sane, i.e. there is no write to it yet
263     mainBuffer.dirty = false;
264     return true;
265     }
266 cebix 1.19
267    
268 gbeauche 1.27 /*
269     * Deinitialize VOSF system
270     */
271 cebix 1.19
272 gbeauche 1.27 static void video_vosf_exit(void)
273     {
274 gbeauche 1.29 if (mainBuffer.pageInfo) {
275     free(mainBuffer.pageInfo);
276     mainBuffer.pageInfo = NULL;
277 gbeauche 1.27 }
278 gbeauche 1.29 if (mainBuffer.dirtyPages) {
279     free(mainBuffer.dirtyPages);
280     mainBuffer.dirtyPages = NULL;
281 cebix 1.19 }
282     }
283    
284    
285 gbeauche 1.1 /*
286 gbeauche 1.20 * Screen fault handler
287 gbeauche 1.1 */
288    
289 gbeauche 1.33 bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
290 gbeauche 1.1 {
291 gbeauche 1.16 const uintptr addr = (uintptr)fault_address;
292    
293 gbeauche 1.11 /* Someone attempted to write to the frame buffer. Make it writeable
294 gbeauche 1.20 * now so that the data could actually be written to. It will be made
295 gbeauche 1.11 * read-only back in one of the screen update_*() functions.
296     */
297 gbeauche 1.27 if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
298     const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
299 gbeauche 1.11 LOCK_VOSF;
300     PFLAG_SET(page);
301 gbeauche 1.27 vm_protect((char *)(addr & -mainBuffer.pageSize), mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
302 gbeauche 1.13 mainBuffer.dirty = true;
303 gbeauche 1.11 UNLOCK_VOSF;
304 gbeauche 1.16 return true;
305 gbeauche 1.1 }
306    
307 gbeauche 1.11 /* Otherwise, we don't know how to handle the fault, let it crash */
308 gbeauche 1.16 return false;
309 gbeauche 1.1 }
310    
311 gbeauche 1.20
312 gbeauche 1.1 /*
313     * Update display for Windowed mode and VOSF
314     */
315    
316 gbeauche 1.13 // From video_blit.cpp
317     extern void (*Screen_blit)(uint8 * dest, const uint8 * source, uint32 length);
318 gbeauche 1.33 extern bool Screen_blitter_init(XVisualInfo * visual_info, bool native_byte_order, int mac_depth);
319 cebix 1.23 extern uint32 ExpandMap[256];
320 gbeauche 1.13
321 gbeauche 1.12 /* How can we deal with array overrun conditions ?
322    
323     The state of the framebuffer pages that have been touched are maintained
324     in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
325    
326     Terminology
327    
328     "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
329     "CLEAR Page Guard" refers to the page following the Last Page but is always
330     in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
331     Page Guard but is always in the SET state.
332    
333     Rough process
334    
335 gbeauche 1.13 The update routines must determine which pages have to be blitted to the
336 gbeauche 1.12 screen. This job consists in finding the first_page that was touched.
337     i.e. find the next page that is SET. Then, finding how many pages were
338     touched starting from first_page. i.e. find the next page that is CLEAR.
339    
340 gbeauche 1.13 There are two cases to check:
341 gbeauche 1.12
342     - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
343     but it is beyond the valid pageCount value. Therefore, we exit from the
344     update routine.
345    
346     - Last Page is SET: first_page equals (pageCount - 1) and
347     find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
348     page to the screen. On the next iteration, page equals pageCount and
349     find_next_page_set() will reach the SET Page Guard. We still safely exit
350     from the update routine because the SET Page Guard position is greater
351     than pageCount.
352     */
353    
354 gbeauche 1.33 static inline void update_display_window_vosf(VIDEO_DRV_INIT)
355 gbeauche 1.1 {
356 gbeauche 1.33 VIDEO_MODE_INIT;
357 cebix 1.31
358 gbeauche 1.1 int page = 0;
359     for (;;) {
360 cebix 1.28 const unsigned first_page = find_next_page_set(page);
361 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
362 gbeauche 1.1 break;
363 gbeauche 1.11
364     page = find_next_page_clear(first_page);
365     PFLAG_CLEAR_RANGE(first_page, page);
366 cebix 1.7
367 gbeauche 1.1 // Make the dirty pages read-only again
368     const int32 offset = first_page << mainBuffer.pageBits;
369     const uint32 length = (page - first_page) << mainBuffer.pageBits;
370 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
371 gbeauche 1.1
372     // There is at least one line to update
373     const int y1 = mainBuffer.pageInfo[first_page].top;
374     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
375     const int height = y2 - y1 + 1;
376    
377 gbeauche 1.33 if (VIDEO_MODE_DEPTH < VIDEO_DEPTH_8BIT) {
378 cebix 1.6
379     // Update the_host_buffer and copy of the_buffer
380 gbeauche 1.33 const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
381     const int dst_bytes_per_row = VIDEO_DRV_IMAGE->bytes_per_line;
382     const int pixels_per_byte = VIDEO_MODE_X / src_bytes_per_row;
383 cebix 1.24 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
384 cebix 1.6 for (j = y1; j <= y2; j++) {
385 gbeauche 1.33 Screen_blit(the_host_buffer + i2, the_buffer + i1, VIDEO_MODE_X / pixels_per_byte);
386 cebix 1.24 i1 += src_bytes_per_row;
387     i2 += dst_bytes_per_row;
388 cebix 1.6 }
389    
390     } else {
391    
392     // Update the_host_buffer and copy of the_buffer
393 gbeauche 1.33 const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
394     const int dst_bytes_per_row = VIDEO_DRV_IMAGE->bytes_per_line;
395     const int bytes_per_pixel = src_bytes_per_row / VIDEO_MODE_X;
396 cebix 1.23 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
397 cebix 1.6 for (j = y1; j <= y2; j++) {
398 gbeauche 1.33 Screen_blit(the_host_buffer + i2, the_buffer + i1, bytes_per_pixel * VIDEO_MODE_X);
399 cebix 1.23 i1 += src_bytes_per_row;
400     i2 += dst_bytes_per_row;
401 cebix 1.6 }
402 gbeauche 1.1 }
403 cebix 1.15
404 gbeauche 1.33 if (VIDEO_DRV_HAVE_SHM)
405     XShmPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height, 0);
406 gbeauche 1.1 else
407 gbeauche 1.33 XPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height);
408 gbeauche 1.1 }
409 gbeauche 1.13 mainBuffer.dirty = false;
410 gbeauche 1.1 }
411    
412    
413     /*
414     * Update display for DGA mode and VOSF
415 gbeauche 1.20 * (only in Real or Direct Addressing mode)
416 gbeauche 1.1 */
417    
418     #if REAL_ADDRESSING || DIRECT_ADDRESSING
419     static inline void update_display_dga_vosf(void)
420     {
421 gbeauche 1.33 VIDEO_MODE_INIT;
422 cebix 1.31
423 gbeauche 1.1 int page = 0;
424     for (;;) {
425 cebix 1.28 const unsigned first_page = find_next_page_set(page);
426 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
427 gbeauche 1.1 break;
428 gbeauche 1.11
429     page = find_next_page_clear(first_page);
430     PFLAG_CLEAR_RANGE(first_page, page);
431    
432 gbeauche 1.1 // Make the dirty pages read-only again
433     const int32 offset = first_page << mainBuffer.pageBits;
434     const uint32 length = (page - first_page) << mainBuffer.pageBits;
435 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
436 gbeauche 1.1
437     // I am sure that y2 >= y1 and depth != 1
438     const int y1 = mainBuffer.pageInfo[first_page].top;
439     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
440    
441 gbeauche 1.33 const int bytes_per_row = VIDEO_MODE_ROW_BYTES;
442     const int bytes_per_pixel = VIDEO_MODE_ROW_BYTES / VIDEO_MODE_X;
443 gbeauche 1.1 int i, j;
444    
445     // Check for first column from left and first column
446     // from right that have changed
447 gbeauche 1.33 int x1 = VIDEO_MODE_X * bytes_per_pixel - 1;
448 gbeauche 1.1 for (j = y1; j <= y2; j++) {
449     uint8 * const p1 = &the_buffer[j * bytes_per_row];
450     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
451     for (i = 0; i < x1; i++) {
452     if (p1[i] != p2[i]) {
453     x1 = i;
454     break;
455     }
456     }
457     }
458     x1 /= bytes_per_pixel;
459    
460     int x2 = x1 * bytes_per_pixel;
461     for (j = y2; j >= y1; j--) {
462     uint8 * const p1 = &the_buffer[j * bytes_per_row];
463     uint8 * const p2 = &the_buffer_copy[j * bytes_per_row];
464 gbeauche 1.33 for (i = VIDEO_MODE_X * bytes_per_pixel - 1; i > x2; i--) {
465 gbeauche 1.1 if (p1[i] != p2[i]) {
466     x2 = i;
467     break;
468     }
469     }
470     }
471     x2 /= bytes_per_pixel;
472    
473     // Update the_host_buffer and copy of the_buffer
474     // There should be at least one pixel to copy
475     const int width = x2 - x1 + 1;
476     i = y1 * bytes_per_row + x1 * bytes_per_pixel;
477     for (j = y1; j <= y2; j++) {
478 gbeauche 1.13 Screen_blit(the_host_buffer + i, the_buffer + i, bytes_per_pixel * width);
479 gbeauche 1.1 memcpy(the_buffer_copy + i, the_buffer + i, bytes_per_pixel * width);
480     i += bytes_per_row;
481     }
482     }
483 gbeauche 1.13 mainBuffer.dirty = false;
484 gbeauche 1.1 }
485     #endif
486    
487     #endif /* ENABLE_VOSF */
488    
489     #endif /* VIDEO_VOSF_H */