rename the directory
[AROS.git] / rom / hidds / graphics / sync.c
blobbc164dcc17f81913c9907a80330f36558725be66
1 /*
2 Copyright © 1995-2013, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Sync info class
6 Lang: English.
7 */
9 /****************************************************************************************/
11 #include <proto/alib.h>
12 #include <proto/graphics.h>
13 #include <proto/oop.h>
14 #include <proto/utility.h>
15 #include <exec/memory.h>
16 #include <graphics/gfxnodes.h>
17 #include <oop/oop.h>
18 #include <utility/tagitem.h>
19 #include <hidd/graphics.h>
21 #define DEBUG 0
22 #include <aros/debug.h>
24 #include <stdio.h>
26 #include "graphics_intern.h"
28 #undef csd
29 #define GfxBase csd->cs_GfxBase
31 /****************************************************************************************/
33 static BOOL parse_sync_tags(struct class_static_data *csd, struct sync_data *data, struct TagItem *tags, BOOL init);
35 /****************************************************************************************/
37 OOP_Object *Sync__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg)
39 struct class_static_data *csd = CSD(cl);
40 struct Library *UtilityBase = csd->cs_UtilityBase;
41 struct Library *OOPBase = csd->cs_OOPBase;
42 BOOL ok = TRUE;
44 EnterFunc(bug("Sync::New()\n"));
47 * We need graphics.library in order to be able to create MonitorSpec.
48 * we do it here because graphics.hidd is initialized before
49 * graphics.library
51 ObtainSemaphore(&csd->sema);
53 if (!GfxBase)
55 GfxBase = (void *)OpenLibrary("graphics.library", 41);
56 if (!GfxBase)
57 ok = FALSE;
59 ReleaseSemaphore(&csd->sema);
61 if (!ok)
62 return NULL;
64 /* Get object from superclass */
65 o = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
66 if (o)
68 struct sync_data *data = OOP_INST_DATA(cl, o);
69 struct TagItem *tstate = msg->attrList;
70 char *s = NULL;
71 ULONG board = 0;
72 struct TagItem *tag;
74 /* Parse mandatory attributes */
75 while ((tag = NextTagItem(&tstate)))
77 ULONG idx;
79 if (IS_SYNC_ATTR(tag->ti_Tag, idx))
81 switch (idx)
83 case aoHidd_Sync_HDisp:
84 data->hdisp = tag->ti_Data;
85 break;
87 case aoHidd_Sync_VDisp:
88 data->vdisp = tag->ti_Data;
89 break;
91 case aoHidd_Sync_Flags:
92 data->flags = tag->ti_Data;
93 break;
95 case aoHidd_Sync_Description:
96 s = (char *)tag->ti_Data;
97 break;
99 case aoHidd_Sync_BoardNumber:
100 board = tag->ti_Data;
101 break;
103 case aoHidd_Sync_Variable:
104 if (tag->ti_Data)
105 data->InternalFlags |= SYNC_VARIABLE;
106 break;
108 case aoHidd_Sync_MonitorSpec:
109 data->mspc = (struct MonitorSpec *)tag->ti_Data;
110 break;
112 case aoHidd_Sync_GfxHidd:
113 data->gfxhidd = (OOP_Object *)tag->ti_Data;
114 break;
119 /* We must have HDisp, VDisp and GfxHidd */
120 if ((!data->hdisp) || (!data->vdisp) || (!data->gfxhidd))
121 ok = FALSE;
123 if (ok && (!data->mspc))
126 * We must have a MonitorSpec. Either it's pre-cooked by the driver
127 * (useful for Amiga(tm) chipset), or we create it ourselves
129 data->mspc = (struct MonitorSpec *)GfxNew(MONITOR_SPEC_TYPE);
130 if (data->mspc)
132 data->mspc->ms_Node.xln_Name = data->description;
133 data->mspc->ratioh = RATIO_UNITY;
134 data->mspc->ratiov = RATIO_UNITY;
135 InitSemaphore(&data->mspc->DisplayInfoDataBaseSemaphore);
137 data->InternalFlags |= SYNC_FREE_MONITORSPEC;
139 else
140 ok = FALSE;
143 if (ok)
145 /* By default minimum/maximum bitmap size is equal to display size */
146 data->hmin = data->hdisp;
147 data->vmin = data->vdisp;
149 if (GFX__Hidd_Gfx__GetFBModeQuick(csd->gfxhiddclass, data->gfxhidd) == vHidd_FrameBuffer_Mirrored)
151 /* But for mirrored framebuffer mode we can have larger bitmaps */
152 data->hmax = 16384;
153 data->vmax = 16384;
155 else
157 data->hmax = data->hdisp;
158 data->vmax = data->vdisp;
161 /* Now try to override defaults */
162 tstate = msg->attrList;
163 while ((tag = NextTagItem(&tstate)))
165 ULONG idx;
167 Hidd_Sync_Switch(tag->ti_Tag, idx)
169 case aoHidd_Sync_HMin:
170 data->hmin = tag->ti_Data;
171 break;
173 case aoHidd_Sync_HMax:
174 data->hmax = tag->ti_Data;
175 break;
177 case aoHidd_Sync_VMin:
178 data->vmin = tag->ti_Data;
179 break;
181 case aoHidd_Sync_VMax:
182 data->vmax = tag->ti_Data;
183 break;
187 /* Format description */
188 if (s)
190 char *d = data->description;
191 int dlen = sizeof(data->description);
192 char c;
193 int l;
195 for (;;)
197 c = *s++;
198 if (c == '%')
200 /* It's a format prefix, let's deal with it */
201 c = *s++;
202 switch (c)
204 case 'b':
205 l = snprintf(d, dlen, "%u", (unsigned)board);
206 break;
208 case 'h':
209 l = snprintf(d, dlen, "%u", (unsigned)data->hdisp);
210 break;
212 case 'v':
213 l = snprintf(d, dlen, "%u", (unsigned)data->vdisp);
214 break;
216 default:
217 /* Just copy over two chars */
218 d[0] = '%';
219 l = 1;
220 /* Copy next character only if we have room for it */
221 if (dlen > 2)
223 d[1] = c;
224 l++;
226 break;
229 else
231 /* Copy one character */
232 *d = c;
233 l = 1;
236 /* If NULL byte has been just transferred, exit, the string is already terminated */
237 if (!c)
238 break;
240 /* Increment pointer, decrement length */
241 d += l;
242 dlen -= l;
244 /* If we have only one byte in the destination left, terminate the string and exit */
245 if (dlen < 2)
247 *d = 0;
248 break;
251 } /* if (s) */
253 ok = parse_sync_tags(CSD(cl), data, msg->attrList, TRUE);
254 } /* if (ok) */
256 if (ok)
258 /* Set object pointer and add the MonitorSpec to the GfxBase->MonitorList */
259 data->mspc->ms_Object = (void *)o;
261 ObtainSemaphore(GfxBase->MonitorListSemaphore);
262 Enqueue(&GfxBase->MonitorList, (struct Node*)data->mspc);
263 ReleaseSemaphore(GfxBase->MonitorListSemaphore);
265 return o;
267 else
269 OOP_MethodID dispose_mid;
271 D(bug("!!! ERROR PARSING SYNC ATTRS IN Sync::New() !!!\n"));
273 dispose_mid = OOP_GetMethodID(IID_Root, moRoot_Dispose);
274 OOP_CoerceMethod(cl, o, &dispose_mid);
278 return NULL;
281 /****************************************************************************************/
283 VOID Sync__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
285 struct sync_data *data = OOP_INST_DATA(cl, o);
286 struct class_static_data *csd = CSD(cl);
288 /* First we remove the MonitorSpec from the list, if it's our MonitorSpec
289 and it's in the list */
290 if ((data->InternalFlags & SYNC_FREE_MONITORSPEC) &&
291 data->mspc->ms_Node.xln_Succ) {
293 ObtainSemaphore(GfxBase->MonitorListSemaphore);
294 Remove((struct Node *)data->mspc);
295 ReleaseSemaphore(GfxBase->MonitorListSemaphore);
298 /* Then we dispose things that we created */
299 if (data->InternalFlags & SYNC_FREE_SPECIALMONITOR)
300 GfxFree(&data->mspc->ms_Special->spm_Node);
301 if (data->InternalFlags & SYNC_FREE_MONITORSPEC)
302 GfxFree(&data->mspc->ms_Node);
304 OOP_DoSuperMethod(cl, o, msg);
307 /****************************************************************************************/
309 VOID Sync__Root__Get(OOP_Class *cl, OOP_Object *o, struct pRoot_Get *msg)
311 struct sync_data *data;
312 struct class_static_data *csd;
313 ULONG idx;
315 data = OOP_INST_DATA(cl, o);
316 csd = CSD(cl);
318 if (IS_SYNC_ATTR(msg->attrID, idx))
320 UWORD hsync_start, hsync_end, vsync_start, vsync_end;
322 if (data->mspc->ms_Special) {
323 hsync_start = data->mspc->ms_Special->hsync.asi_Start;
324 hsync_end = data->mspc->ms_Special->hsync.asi_Stop;
325 vsync_start = data->mspc->ms_Special->vsync.asi_Start;
326 vsync_end = data->mspc->ms_Special->vsync.asi_Stop;
327 } else {
328 /* Special failback values that will result in zero margins and sync lengths */
329 hsync_start = data->hdisp;
330 hsync_end = data->hdisp;
331 vsync_start = data->vdisp;
332 vsync_end = data->vdisp;
335 switch (idx)
337 case aoHidd_Sync_PixelTime:
338 if (data->pixelclock) {
339 ULONG khz = data->pixelclock / 1000;
341 *msg->storage = 1000000000 / khz;
342 } else
343 *msg->storage = 0;
345 case aoHidd_Sync_PixelClock:
346 *msg->storage = data->pixelclock;
347 break;
349 case aoHidd_Sync_LeftMargin:
350 *msg->storage = data->htotal - hsync_end;
351 break;
353 case aoHidd_Sync_RightMargin:
354 *msg->storage = hsync_start - data->hdisp;
355 break;
357 case aoHidd_Sync_HSyncLength:
358 *msg->storage = hsync_end - hsync_start;
359 break;
361 case aoHidd_Sync_UpperMargin:
362 *msg->storage = data->mspc->total_rows - vsync_end;
363 break;
365 case aoHidd_Sync_LowerMargin:
366 *msg->storage = vsync_end - data->vdisp;
367 break;
369 case aoHidd_Sync_VSyncLength:
370 *msg->storage = vsync_end - vsync_start;
371 break;
373 case aoHidd_Sync_HDisp:
374 *msg->storage = data->hdisp;
375 break;
377 case aoHidd_Sync_VDisp:
378 *msg->storage = data->vdisp;
379 break;
381 case aoHidd_Sync_HSyncStart:
382 *msg->storage = hsync_start;
383 break;
385 case aoHidd_Sync_HSyncEnd:
386 *msg->storage = hsync_end;
387 break;
389 case aoHidd_Sync_HTotal:
390 *msg->storage = data->htotal;
391 break;
393 case aoHidd_Sync_VSyncStart:
394 *msg->storage = vsync_start;
395 break;
397 case aoHidd_Sync_VSyncEnd:
398 *msg->storage = vsync_end;
399 break;
401 case aoHidd_Sync_VTotal:
402 *msg->storage = data->mspc->total_rows;
403 break;
405 case aoHidd_Sync_Description:
406 *msg->storage = (IPTR)data->description;
407 break;
409 case aoHidd_Sync_HMin:
410 *msg->storage = data->hmin;
411 break;
413 case aoHidd_Sync_HMax:
414 *msg->storage = data->hmax;
415 break;
417 case aoHidd_Sync_VMin:
418 *msg->storage = data->vmin;
419 break;
421 case aoHidd_Sync_VMax:
422 *msg->storage = data->vmax;
423 break;
425 case aoHidd_Sync_Flags:
426 *msg->storage = data->flags;
427 break;
429 case aoHidd_Sync_Variable:
430 *msg->storage = (data->InternalFlags & SYNC_VARIABLE) ? TRUE : FALSE;
431 break;
433 case aoHidd_Sync_MonitorSpec:
434 *msg->storage = (IPTR)data->mspc;
435 break;
437 case aoHidd_Sync_GfxHidd:
438 *msg->storage = (IPTR)data->gfxhidd;
439 break;
441 default:
442 D(bug("!!! TRYING TO GET UNKNOWN ATTR FROM SYNC OBJECT !!!\n"));
443 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
444 break;
449 else
450 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
453 /****************************************************************************************/
455 void Sync__Root__Set(OOP_Class *cl, OOP_Object *o, struct pRoot_Set *msg)
457 struct sync_data *data = OOP_INST_DATA(cl, o);
459 /* Set actually works only if the object is variable */
460 if (data->InternalFlags & SYNC_VARIABLE)
462 struct class_static_data *csd = CSD(cl);
463 BOOL notify_driver = parse_sync_tags(csd, data, msg->attrList, FALSE);
465 if (notify_driver)
466 HIDD_Gfx_SetMode(data->gfxhidd, (OOP_Object *)data->mspc->ms_Object);
469 OOP_DoSuperMethod(cl, o, &msg->mID);
472 /****************************************************************************************/
475 * A backwards compatibility callback that recalculates new htotal value from
476 * MonitorSpec->total_colorclocks.
477 * Future sync editor for AROS will not have to use this API. Instead it is
478 * suggested to use OOP API on a sync object, obtained via VecInfo. It is
479 * more straightforward and flexible.
482 static LONG do_monitor(struct MonitorSpec *mspc)
484 /* Extract sync object data from the MonitorSpec */
485 OOP_Object *obj = (OOP_Object *)mspc->ms_Object;
486 OOP_Class *cl = OOP_OCLASS(obj);
487 struct sync_data *data = OOP_INST_DATA(cl, obj);
488 struct class_static_data *csd = CSD(cl);
490 data->htotal = 100000000 / mspc->total_colorclocks / 28 / data->pixelclock;
491 HIDD_Gfx_SetMode(data->gfxhidd, obj);
493 return 0;
496 /****************************************************************************************/
499 * Parses the tags supplied in 'tags' and puts the result into 'data'.
500 * It also checks to see if all needed attrs are supplied.
501 * Return value is treated as:
502 * - Creation time (init = TRUE) - success/failure flag.
503 * - Set method (init = FALSE) - TRUE if something changed and display driver needs to be notified.
505 static BOOL parse_sync_tags(struct class_static_data *csd, struct sync_data *data, struct TagItem *tags, BOOL init)
507 struct Library *UtilityBase = csd->cs_UtilityBase;
508 UWORD hsync_start = 0;
509 UWORD vsync_start = 0;
510 UWORD hsync_end = data->hdisp;
511 UWORD vsync_end = data->vdisp;
512 BOOL have_hsync_start = FALSE;
513 BOOL have_hsync_end = FALSE;
514 BOOL have_vsync_start = FALSE;
515 BOOL have_vsync_end = FALSE;
516 BOOL have_htotal = FALSE;
517 BOOL have_vtotal = FALSE;
518 BOOL change_totclk = init;
519 BOOL notify_driver = FALSE;
520 struct TagItem *tag, *tstate = tags;
523 * Parse sync signal parameters. They may come either as start, stop and total
524 * values (which most of drivers use), or as LinuxFB-style specification (margins and
525 * sync length.
526 * The latter specification is deprecated since no drivers except LinuxFB (which is
527 * broken anyway) use it.
529 while ((tag = NextTagItem(&tstate)))
531 ULONG idx;
533 if (IS_SYNC_ATTR(tag->ti_Tag, idx))
535 switch (idx)
537 case aoHidd_Sync_PixelClock:
538 data->pixelclock = tag->ti_Data;
539 change_totclk = TRUE;
540 break;
542 case aoHidd_Sync_PixelTime:
544 * According to the HOWTO, PixelTime is one million divided by pixelclock in mHz.
545 * Pixelclock is not always a multiple of 1 mHz, but it seems to always be a multiple
546 * of 1 kHz. We rely on this fact in order to be able to calculate everything in integers.
547 * Anyway, this attribute is deprecated, don't use it.
548 * I intentionally don't simplify this expression in order to make it clear. Let's leave
549 * it to the compiler - sonic.
551 data->pixelclock = (1000000000 / tag->ti_Data) * 1000;
552 change_totclk = TRUE;
553 break;
555 case aoHidd_Sync_HSyncStart:
556 hsync_start = tag->ti_Data;
557 have_hsync_start = TRUE;
558 break;
560 case aoHidd_Sync_HSyncEnd:
561 hsync_end = tag->ti_Data;
562 have_hsync_end = TRUE;
563 break;
565 case aoHidd_Sync_VSyncStart:
566 vsync_start = tag->ti_Data;
567 have_vsync_start = TRUE;
568 break;
570 case aoHidd_Sync_VSyncEnd:
571 vsync_end = tag->ti_Data;
572 have_vsync_end = TRUE;
573 break;
575 case aoHidd_Sync_HTotal:
576 data->htotal = tag->ti_Data;
577 have_htotal = TRUE;
578 change_totclk = TRUE;
579 break;
581 case aoHidd_Sync_VTotal:
582 data->mspc->total_rows = tag->ti_Data;
583 have_vtotal = TRUE;
584 notify_driver = TRUE;
585 break;
590 D(bug("[sync] PixelClock is set to %u\n", data->pixelclock));
593 * Old LFB-style specification needs to be processed in a particular order,
594 * so we do it here, if needed.
596 if (!have_hsync_start)
598 tag = FindTagItem(aHidd_Sync_RightMargin, tags);
599 if (tag)
601 hsync_start = data->hdisp + tag->ti_Data;
602 have_hsync_start = TRUE;
606 if (!have_hsync_end)
608 tag = FindTagItem(aHidd_Sync_HSyncLength, tags);
609 if (tag)
611 hsync_end = hsync_start + tag->ti_Data;
612 have_hsync_end = TRUE;
616 if (!have_vsync_start)
618 tag = FindTagItem(aHidd_Sync_LowerMargin, tags);
619 if (tag)
621 vsync_start = data->vdisp + tag->ti_Data;
622 have_vsync_start = TRUE;
626 if (!have_vsync_end)
628 tag = FindTagItem(aHidd_Sync_VSyncLength, tags);
629 if (tag)
631 vsync_end = vsync_start + tag->ti_Data;
632 have_vsync_end = TRUE;
636 if (have_hsync_start || have_hsync_end || have_vsync_start || have_vsync_end)
638 /* Sync data changed */
639 if (init)
641 /* During object creation this means we need to attach SpecialMonitor to our MonitorSpec. */
642 if (!data->mspc->ms_Special)
644 data->mspc->ms_Special = (struct SpecialMonitor *)GfxNew(SPECIAL_MONITOR_TYPE);
645 if (!data->mspc->ms_Special)
646 return FALSE;
648 if (data->InternalFlags & SYNC_VARIABLE)
649 data->mspc->ms_Special->do_monitor = do_monitor;
651 data->mspc->ms_Flags |= MSF_REQUEST_SPECIAL;
652 data->InternalFlags |= SYNC_FREE_SPECIALMONITOR;
655 /* Notification is needed */
656 notify_driver = TRUE;
659 if (data->mspc->ms_Special)
661 if (have_hsync_start)
662 data->mspc->ms_Special->hsync.asi_Start = hsync_start;
663 if (have_hsync_end)
664 data->mspc->ms_Special->hsync.asi_Stop = hsync_end;
665 if (have_vsync_start)
666 data->mspc->ms_Special->vsync.asi_Start = vsync_start;
667 if (have_vsync_end)
668 data->mspc->ms_Special->vsync.asi_Stop = vsync_end;
671 if (!have_htotal)
673 UWORD left_margin = 0;
675 tag = FindTagItem(aHidd_Sync_LeftMargin, tags);
676 if (tag)
678 left_margin = tag->ti_Data;
679 change_totclk = TRUE;
682 * If we have neither HTotal nor LeftMargin, htotal will be equal to hsync_end here.
683 * Previously, hsync_end gets equal to hdisp if no horizontal sync data was specified.
684 * This is done for poor man's drivers which can't provide complete sync information
685 * (like hosted drivers, especially SDL). In this case total = disp, it's better than
686 * nothing. The same is done below with vtotal.
688 data->htotal = hsync_end + left_margin;
691 if (!have_vtotal)
693 UWORD upper_margin = 0;
695 tag = FindTagItem(aHidd_Sync_UpperMargin, tags);
696 if (tag)
698 upper_margin = tag->ti_Data;
699 notify_driver = TRUE;
701 data->mspc->total_rows = vsync_end + upper_margin;
704 if (change_totclk)
706 if (data->pixelclock)
707 data->mspc->total_colorclocks = 100000000 / (data->pixelclock / data->htotal * 28);
708 else
711 * Another kludge for drivers without sync data. Amiga software never expects
712 * to get zero in total_colorclocks, so we have to fill it in with something.
713 * This value will have totally nothing to do with real display refresh rate,
714 * but we can do nothing with it
716 data->mspc->total_colorclocks = VGA_COLORCLOCKS;
718 /* change_totclk always implies notify_driver, for code simplicity */
719 notify_driver = TRUE;
722 return notify_driver;