revert commit 56204.
[AROS.git] / rom / hidds / gfx / gfx_syncclass.c
blobc960b7c6b65883d92f66bb2d45158e1e704ee089
1 /*
2 Copyright © 1995-2017, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Sync info class
6 Lang: English.
7 */
9 /****************************************************************************************/
11 #include "gfx_debug.h"
13 #include <proto/alib.h>
14 #include <proto/graphics.h>
15 #include <proto/oop.h>
16 #include <proto/utility.h>
17 #include <exec/memory.h>
18 #include <graphics/gfxnodes.h>
19 #include <oop/oop.h>
20 #include <utility/tagitem.h>
21 #include <hidd/gfx.h>
23 #include <stdio.h>
25 #include "gfx_intern.h"
27 #undef csd
28 #define GfxBase csd->cs_GfxBase
30 /****************************************************************************************/
32 static BOOL parse_sync_tags(struct class_static_data *csd, struct sync_data *data, struct TagItem *tags, BOOL init);
34 /****************************************************************************************/
36 OOP_Object *Sync__Root__New(OOP_Class *cl, OOP_Object *o, struct pRoot_New *msg)
38 struct class_static_data *csd = CSD(cl);
39 struct Library *UtilityBase = csd->cs_UtilityBase;
40 struct Library *OOPBase = csd->cs_OOPBase;
41 BOOL ok = TRUE;
43 EnterFunc(bug("Sync::New()\n"));
46 * We need graphics.library in order to be able to create MonitorSpec.
47 * we do it here because gfx.hidd is initialized before
48 * graphics.library
50 ObtainSemaphore(&csd->sema);
52 if (!GfxBase)
54 GfxBase = (void *)OpenLibrary("graphics.library", 41);
55 if (!GfxBase)
56 ok = FALSE;
58 ReleaseSemaphore(&csd->sema);
60 if (!ok)
61 return NULL;
63 /* Get object from superclass */
64 o = (OOP_Object *)OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
65 if (o)
67 struct sync_data *data = OOP_INST_DATA(cl, o);
68 struct TagItem *tstate = msg->attrList;
69 char *s = NULL;
70 ULONG board = 0;
71 struct TagItem *tag;
73 /* Parse mandatory attributes */
74 while ((tag = NextTagItem(&tstate)))
76 ULONG idx;
78 if (IS_SYNC_ATTR(tag->ti_Tag, idx))
80 switch (idx)
82 case aoHidd_Sync_HDisp:
83 data->hdisp = tag->ti_Data;
84 break;
86 case aoHidd_Sync_VDisp:
87 data->vdisp = tag->ti_Data;
88 break;
90 case aoHidd_Sync_Flags:
91 data->flags = tag->ti_Data;
92 break;
94 case aoHidd_Sync_Description:
95 s = (char *)tag->ti_Data;
96 break;
98 case aoHidd_Sync_BoardNumber:
99 board = tag->ti_Data;
100 break;
102 case aoHidd_Sync_Variable:
103 if (tag->ti_Data)
104 data->InternalFlags |= SYNC_VARIABLE;
105 break;
107 case aoHidd_Sync_MonitorSpec:
108 data->mspc = (struct MonitorSpec *)tag->ti_Data;
109 break;
111 case aoHidd_Sync_GfxHidd:
112 data->gfxhidd = (OOP_Object *)tag->ti_Data;
113 break;
118 /* We must have HDisp, VDisp and GfxHidd */
119 if ((!data->hdisp) || (!data->vdisp) || (!data->gfxhidd))
120 ok = FALSE;
122 if (ok && (!data->mspc))
125 * We must have a MonitorSpec. Either it's pre-cooked by the driver
126 * (useful for Amiga(tm) chipset), or we create it ourselves
128 data->mspc = (struct MonitorSpec *)GfxNew(MONITOR_SPEC_TYPE);
129 if (data->mspc)
131 data->mspc->ms_Node.xln_Name = data->description;
132 data->mspc->ratioh = RATIO_UNITY;
133 data->mspc->ratiov = RATIO_UNITY;
134 InitSemaphore(&data->mspc->DisplayInfoDataBaseSemaphore);
136 data->InternalFlags |= SYNC_FREE_MONITORSPEC;
138 else
139 ok = FALSE;
142 if (ok)
144 /* By default minimum/maximum bitmap size is equal to display size */
145 data->hmin = data->hdisp;
146 data->vmin = data->vdisp;
148 if (GFXHIDD__Hidd_Gfx__GetFBModeQuick(csd->gfxhiddclass, data->gfxhidd) == vHidd_FrameBuffer_Mirrored)
150 /* But for mirrored framebuffer mode we can have larger bitmaps */
151 data->hmax = 16384;
152 data->vmax = 16384;
154 else
156 data->hmax = data->hdisp;
157 data->vmax = data->vdisp;
160 /* Now try to override defaults */
161 tstate = msg->attrList;
162 while ((tag = NextTagItem(&tstate)))
164 ULONG idx;
166 Hidd_Sync_Switch(tag->ti_Tag, idx)
168 case aoHidd_Sync_HMin:
169 data->hmin = tag->ti_Data;
170 break;
172 case aoHidd_Sync_HMax:
173 data->hmax = tag->ti_Data;
174 break;
176 case aoHidd_Sync_VMin:
177 data->vmin = tag->ti_Data;
178 break;
180 case aoHidd_Sync_VMax:
181 data->vmax = tag->ti_Data;
182 break;
186 /* Format description */
187 if (s)
189 char *d = data->description;
190 int dlen = sizeof(data->description);
191 char c;
192 int l;
194 for (;;)
196 c = *s++;
197 if (c == '%')
199 /* It's a format prefix, let's deal with it */
200 c = *s++;
201 switch (c)
203 case 'b':
204 l = snprintf(d, dlen, "%u", (unsigned)board);
205 break;
207 case 'h':
208 l = snprintf(d, dlen, "%u", (unsigned)data->hdisp);
209 break;
211 case 'v':
212 l = snprintf(d, dlen, "%u", (unsigned)data->vdisp);
213 break;
215 default:
216 /* Just copy over two chars */
217 d[0] = '%';
218 l = 1;
219 /* Copy next character only if we have room for it */
220 if (dlen > 2)
222 d[1] = c;
223 l++;
225 break;
228 else
230 /* Copy one character */
231 *d = c;
232 l = 1;
235 /* If NULL byte has been just transferred, exit, the string is already terminated */
236 if (!c)
237 break;
239 /* Increment pointer, decrement length */
240 d += l;
241 dlen -= l;
243 /* If we have only one byte in the destination left, terminate the string and exit */
244 if (dlen < 2)
246 *d = 0;
247 break;
250 } /* if (s) */
252 ok = parse_sync_tags(CSD(cl), data, msg->attrList, TRUE);
253 } /* if (ok) */
255 if (ok)
257 /* Set object pointer and add the MonitorSpec to the GfxBase->MonitorList */
258 data->mspc->ms_Object = (void *)o;
260 ObtainSemaphore(GfxBase->MonitorListSemaphore);
261 Enqueue(&GfxBase->MonitorList, (struct Node*)data->mspc);
262 ReleaseSemaphore(GfxBase->MonitorListSemaphore);
264 return o;
266 else
268 OOP_MethodID dispose_mid;
270 D(bug("!!! ERROR PARSING SYNC ATTRS IN Sync::New() !!!\n"));
272 dispose_mid = OOP_GetMethodID(IID_Root, moRoot_Dispose);
273 OOP_CoerceMethod(cl, o, &dispose_mid);
277 return NULL;
280 /****************************************************************************************/
282 VOID Sync__Root__Dispose(OOP_Class *cl, OOP_Object *o, OOP_Msg msg)
284 struct sync_data *data = OOP_INST_DATA(cl, o);
285 struct class_static_data *csd = CSD(cl);
287 /* First we remove the MonitorSpec from the list, if it's our MonitorSpec
288 and it's in the list */
289 if ((data->InternalFlags & SYNC_FREE_MONITORSPEC) &&
290 data->mspc->ms_Node.xln_Succ) {
292 ObtainSemaphore(GfxBase->MonitorListSemaphore);
293 Remove((struct Node *)data->mspc);
294 ReleaseSemaphore(GfxBase->MonitorListSemaphore);
297 /* Then we dispose things that we created */
298 if (data->InternalFlags & SYNC_FREE_SPECIALMONITOR)
299 GfxFree(&data->mspc->ms_Special->spm_Node);
300 if (data->InternalFlags & SYNC_FREE_MONITORSPEC)
301 GfxFree(&data->mspc->ms_Node);
303 OOP_DoSuperMethod(cl, o, msg);
306 /****************************************************************************************/
308 VOID Sync__Root__Get(OOP_Class *cl, OOP_Object *o, struct pRoot_Get *msg)
310 struct sync_data *data;
311 struct class_static_data *csd;
312 ULONG idx;
314 data = OOP_INST_DATA(cl, o);
315 csd = CSD(cl);
317 if (IS_SYNC_ATTR(msg->attrID, idx))
319 UWORD hsync_start, hsync_end, vsync_start, vsync_end;
321 if (data->mspc->ms_Special) {
322 hsync_start = data->mspc->ms_Special->hsync.asi_Start;
323 hsync_end = data->mspc->ms_Special->hsync.asi_Stop;
324 vsync_start = data->mspc->ms_Special->vsync.asi_Start;
325 vsync_end = data->mspc->ms_Special->vsync.asi_Stop;
326 } else {
327 /* Special failback values that will result in zero margins and sync lengths */
328 hsync_start = data->hdisp;
329 hsync_end = data->hdisp;
330 vsync_start = data->vdisp;
331 vsync_end = data->vdisp;
334 switch (idx)
336 case aoHidd_Sync_PixelTime:
337 if (data->pixelclock) {
338 ULONG khz = data->pixelclock / 1000;
340 *msg->storage = 1000000000 / khz;
341 } else
342 *msg->storage = 0;
344 case aoHidd_Sync_PixelClock:
345 *msg->storage = data->pixelclock;
346 break;
348 case aoHidd_Sync_LeftMargin:
349 *msg->storage = data->htotal - hsync_end;
350 break;
352 case aoHidd_Sync_RightMargin:
353 *msg->storage = hsync_start - data->hdisp;
354 break;
356 case aoHidd_Sync_HSyncLength:
357 *msg->storage = hsync_end - hsync_start;
358 break;
360 case aoHidd_Sync_UpperMargin:
361 *msg->storage = data->mspc->total_rows - vsync_end;
362 break;
364 case aoHidd_Sync_LowerMargin:
365 *msg->storage = vsync_end - data->vdisp;
366 break;
368 case aoHidd_Sync_VSyncLength:
369 *msg->storage = vsync_end - vsync_start;
370 break;
372 case aoHidd_Sync_HDisp:
373 *msg->storage = data->hdisp;
374 break;
376 case aoHidd_Sync_VDisp:
377 *msg->storage = data->vdisp;
378 break;
380 case aoHidd_Sync_HSyncStart:
381 *msg->storage = hsync_start;
382 break;
384 case aoHidd_Sync_HSyncEnd:
385 *msg->storage = hsync_end;
386 break;
388 case aoHidd_Sync_HTotal:
389 *msg->storage = data->htotal;
390 break;
392 case aoHidd_Sync_VSyncStart:
393 *msg->storage = vsync_start;
394 break;
396 case aoHidd_Sync_VSyncEnd:
397 *msg->storage = vsync_end;
398 break;
400 case aoHidd_Sync_VTotal:
401 *msg->storage = data->mspc->total_rows;
402 break;
404 case aoHidd_Sync_Description:
405 *msg->storage = (IPTR)data->description;
406 break;
408 case aoHidd_Sync_HMin:
409 *msg->storage = data->hmin;
410 break;
412 case aoHidd_Sync_HMax:
413 *msg->storage = data->hmax;
414 break;
416 case aoHidd_Sync_VMin:
417 *msg->storage = data->vmin;
418 break;
420 case aoHidd_Sync_VMax:
421 *msg->storage = data->vmax;
422 break;
424 case aoHidd_Sync_Flags:
425 *msg->storage = data->flags;
426 break;
428 case aoHidd_Sync_Variable:
429 *msg->storage = (data->InternalFlags & SYNC_VARIABLE) ? TRUE : FALSE;
430 break;
432 case aoHidd_Sync_MonitorSpec:
433 *msg->storage = (IPTR)data->mspc;
434 break;
436 case aoHidd_Sync_GfxHidd:
437 *msg->storage = (IPTR)data->gfxhidd;
438 break;
440 default:
441 D(bug("!!! TRYING TO GET UNKNOWN ATTR FROM SYNC OBJECT !!!\n"));
442 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
443 break;
448 else
449 OOP_DoSuperMethod(cl, o, (OOP_Msg)msg);
452 /****************************************************************************************/
454 void Sync__Root__Set(OOP_Class *cl, OOP_Object *o, struct pRoot_Set *msg)
456 struct sync_data *data = OOP_INST_DATA(cl, o);
458 /* Set actually works only if the object is variable */
459 if (data->InternalFlags & SYNC_VARIABLE)
461 struct class_static_data *csd = CSD(cl);
462 BOOL notify_driver = parse_sync_tags(csd, data, msg->attrList, FALSE);
464 if (notify_driver)
465 HIDD_Gfx_SetMode(data->gfxhidd, (OOP_Object *)data->mspc->ms_Object);
468 OOP_DoSuperMethod(cl, o, &msg->mID);
471 /****************************************************************************************/
474 * A backwards compatibility callback that recalculates new htotal value from
475 * MonitorSpec->total_colorclocks.
476 * Future sync editor for AROS will not have to use this API. Instead it is
477 * suggested to use OOP API on a sync object, obtained via VecInfo. It is
478 * more straightforward and flexible.
481 static LONG do_monitor(struct MonitorSpec *mspc)
483 /* Extract sync object data from the MonitorSpec */
484 OOP_Object *obj = (OOP_Object *)mspc->ms_Object;
485 OOP_Class *cl = OOP_OCLASS(obj);
486 struct sync_data *data = OOP_INST_DATA(cl, obj);
487 struct class_static_data *csd = CSD(cl);
489 data->htotal = 100000000 / mspc->total_colorclocks / 28 / data->pixelclock;
490 HIDD_Gfx_SetMode(data->gfxhidd, obj);
492 return 0;
495 /****************************************************************************************/
498 * Parses the tags supplied in 'tags' and puts the result into 'data'.
499 * It also checks to see if all needed attrs are supplied.
500 * Return value is treated as:
501 * - Creation time (init = TRUE) - success/failure flag.
502 * - Set method (init = FALSE) - TRUE if something changed and display driver needs to be notified.
504 static BOOL parse_sync_tags(struct class_static_data *csd, struct sync_data *data, struct TagItem *tags, BOOL init)
506 struct Library *UtilityBase = csd->cs_UtilityBase;
507 UWORD hsync_start = 0;
508 UWORD vsync_start = 0;
509 UWORD hsync_end = data->hdisp;
510 UWORD vsync_end = data->vdisp;
511 BOOL have_hsync_start = FALSE;
512 BOOL have_hsync_end = FALSE;
513 BOOL have_vsync_start = FALSE;
514 BOOL have_vsync_end = FALSE;
515 BOOL have_htotal = FALSE;
516 BOOL have_vtotal = FALSE;
517 BOOL change_totclk = init;
518 BOOL notify_driver = FALSE;
519 struct TagItem *tag, *tstate = tags;
522 * Parse sync signal parameters. They may come either as start, stop and total
523 * values (which most of drivers use), or as LinuxFB-style specification (margins and
524 * sync length.
525 * The latter specification is deprecated since no drivers except LinuxFB (which is
526 * broken anyway) use it.
528 while ((tag = NextTagItem(&tstate)))
530 ULONG idx;
532 if (IS_SYNC_ATTR(tag->ti_Tag, idx))
534 switch (idx)
536 case aoHidd_Sync_PixelClock:
537 data->pixelclock = tag->ti_Data;
538 change_totclk = TRUE;
539 break;
541 case aoHidd_Sync_PixelTime:
543 * According to the HOWTO, PixelTime is one million divided by pixelclock in mHz.
544 * Pixelclock is not always a multiple of 1 mHz, but it seems to always be a multiple
545 * of 1 kHz. We rely on this fact in order to be able to calculate everything in integers.
546 * Anyway, this attribute is deprecated, don't use it.
547 * I intentionally don't simplify this expression in order to make it clear. Let's leave
548 * it to the compiler - sonic.
550 data->pixelclock = (1000000000 / tag->ti_Data) * 1000;
551 change_totclk = TRUE;
552 break;
554 case aoHidd_Sync_HSyncStart:
555 hsync_start = tag->ti_Data;
556 have_hsync_start = TRUE;
557 break;
559 case aoHidd_Sync_HSyncEnd:
560 hsync_end = tag->ti_Data;
561 have_hsync_end = TRUE;
562 break;
564 case aoHidd_Sync_VSyncStart:
565 vsync_start = tag->ti_Data;
566 have_vsync_start = TRUE;
567 break;
569 case aoHidd_Sync_VSyncEnd:
570 vsync_end = tag->ti_Data;
571 have_vsync_end = TRUE;
572 break;
574 case aoHidd_Sync_HTotal:
575 data->htotal = tag->ti_Data;
576 have_htotal = TRUE;
577 change_totclk = TRUE;
578 break;
580 case aoHidd_Sync_VTotal:
581 data->mspc->total_rows = tag->ti_Data;
582 have_vtotal = TRUE;
583 notify_driver = TRUE;
584 break;
589 D(bug("[sync] PixelClock is set to %u\n", data->pixelclock));
592 * Old LFB-style specification needs to be processed in a particular order,
593 * so we do it here, if needed.
595 if (!have_hsync_start)
597 tag = FindTagItem(aHidd_Sync_RightMargin, tags);
598 if (tag)
600 hsync_start = data->hdisp + tag->ti_Data;
601 have_hsync_start = TRUE;
605 if (!have_hsync_end)
607 tag = FindTagItem(aHidd_Sync_HSyncLength, tags);
608 if (tag)
610 hsync_end = hsync_start + tag->ti_Data;
611 have_hsync_end = TRUE;
615 if (!have_vsync_start)
617 tag = FindTagItem(aHidd_Sync_LowerMargin, tags);
618 if (tag)
620 vsync_start = data->vdisp + tag->ti_Data;
621 have_vsync_start = TRUE;
625 if (!have_vsync_end)
627 tag = FindTagItem(aHidd_Sync_VSyncLength, tags);
628 if (tag)
630 vsync_end = vsync_start + tag->ti_Data;
631 have_vsync_end = TRUE;
635 if (have_hsync_start || have_hsync_end || have_vsync_start || have_vsync_end)
637 /* Sync data changed */
638 if (init)
640 /* During object creation this means we need to attach SpecialMonitor to our MonitorSpec. */
641 if (!data->mspc->ms_Special)
643 data->mspc->ms_Special = (struct SpecialMonitor *)GfxNew(SPECIAL_MONITOR_TYPE);
644 if (!data->mspc->ms_Special)
645 return FALSE;
647 if (data->InternalFlags & SYNC_VARIABLE)
648 data->mspc->ms_Special->do_monitor = do_monitor;
650 data->mspc->ms_Flags |= MSF_REQUEST_SPECIAL;
651 data->InternalFlags |= SYNC_FREE_SPECIALMONITOR;
654 /* Notification is needed */
655 notify_driver = TRUE;
658 if (data->mspc->ms_Special)
660 if (have_hsync_start)
661 data->mspc->ms_Special->hsync.asi_Start = hsync_start;
662 if (have_hsync_end)
663 data->mspc->ms_Special->hsync.asi_Stop = hsync_end;
664 if (have_vsync_start)
665 data->mspc->ms_Special->vsync.asi_Start = vsync_start;
666 if (have_vsync_end)
667 data->mspc->ms_Special->vsync.asi_Stop = vsync_end;
670 if (!have_htotal)
672 UWORD left_margin = 0;
674 tag = FindTagItem(aHidd_Sync_LeftMargin, tags);
675 if (tag)
677 left_margin = tag->ti_Data;
678 change_totclk = TRUE;
681 * If we have neither HTotal nor LeftMargin, htotal will be equal to hsync_end here.
682 * Previously, hsync_end gets equal to hdisp if no horizontal sync data was specified.
683 * This is done for poor man's drivers which can't provide complete sync information
684 * (like hosted drivers, especially SDL). In this case total = disp, it's better than
685 * nothing. The same is done below with vtotal.
687 data->htotal = hsync_end + left_margin;
690 if (!have_vtotal)
692 UWORD upper_margin = 0;
694 tag = FindTagItem(aHidd_Sync_UpperMargin, tags);
695 if (tag)
697 upper_margin = tag->ti_Data;
698 notify_driver = TRUE;
700 data->mspc->total_rows = vsync_end + upper_margin;
703 if (change_totclk)
705 if (data->pixelclock)
706 data->mspc->total_colorclocks = 100000000 / (data->pixelclock / data->htotal * 28);
707 else
710 * Another kludge for drivers without sync data. Amiga software never expects
711 * to get zero in total_colorclocks, so we have to fill it in with something.
712 * This value will have totally nothing to do with real display refresh rate,
713 * but we can do nothing with it
715 data->mspc->total_colorclocks = VGA_COLORCLOCKS;
717 /* change_totclk always implies notify_driver, for code simplicity */
718 notify_driver = TRUE;
721 return notify_driver;