]> Pileus Git - ~andy/gtk/blob - gdk/linux-fb/gdkcolor-fb.c
Implement gdkcc (visually broken, but can be fixed by someone with a clue
[~andy/gtk] / gdk / linux-fb / gdkcolor-fb.c
1 /* GDK - The GIMP Drawing Kit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <time.h>
28 #include <sys/ioctl.h>
29 #include <string.h>
30 #include <stdlib.h>
31
32 #include "gdkcolor.h"
33 #include "gdkprivate-fb.h"
34
35 static gint  gdk_colormap_match_color (GdkColormap *cmap,
36                                        GdkColor    *color,
37                                        const gchar *available);
38
39 static gpointer parent_class;
40
41 static void
42 gdk_colormap_finalize (GObject *object)
43 {
44   GdkColormap *colormap = GDK_COLORMAP(object);
45   GdkColormapPrivateFB *private = (GdkColormapPrivateFB*) colormap;
46
47   if (private->hash)
48     g_hash_table_destroy (private->hash);
49   
50   g_free (private->info);
51   g_free (colormap->colors);
52
53   G_OBJECT_CLASS(parent_class)->finalize(object);
54 }
55
56 static void
57 gdk_colormap_init (GdkColormap *colormap)
58 {
59   colormap->windowing_data = NULL;
60   
61   colormap->size = 0;
62   colormap->colors = NULL;
63 }
64
65 static void
66 gdk_colormap_class_init (GdkColormapClass *klass)
67 {
68   GObjectClass *object_class = G_OBJECT_CLASS (klass);
69
70   parent_class = g_type_class_peek_parent (klass);
71
72   object_class->finalize = gdk_colormap_finalize;
73 }
74
75 GType
76 gdk_colormap_get_type (void)
77 {
78   static GType object_type = 0;
79
80   if (!object_type)
81     {
82       static const GTypeInfo object_info =
83       {
84         sizeof (GdkColormapFBClass),
85         (GBaseInitFunc) NULL,
86         (GBaseFinalizeFunc) NULL,
87         (GClassInitFunc) gdk_colormap_class_init,
88         NULL,           /* class_finalize */
89         NULL,           /* class_data */
90         sizeof (GdkColormapPrivateFB),
91         0,              /* n_preallocs */
92         (GInstanceInitFunc) gdk_colormap_init,
93       };
94       
95       object_type = g_type_register_static (G_TYPE_OBJECT,
96                                             "GdkColormap",
97                                             &object_info);
98     }
99   
100   return object_type;
101 }
102
103 GdkColormap*
104 gdk_colormap_new (GdkVisual *visual,
105                   gint       private_cmap)
106 {
107   GdkColormap *colormap;
108   GdkColormapPrivateFB *private;
109   GdkFBDisplay *fbd;
110   int i;
111
112   g_return_val_if_fail (visual != NULL, NULL);
113
114   private = (GdkColormapPrivateFB *)g_type_create_instance(gdk_colormap_get_type());
115   colormap = (GdkColormap*) private;
116
117   private->base.visual = visual;
118   fbd = gdk_display;
119
120   private->hash = NULL;
121   
122   colormap->size = visual->colormap_size;
123   colormap->colors = NULL;
124
125   switch (visual->type)
126     {
127     case GDK_VISUAL_STATIC_GRAY:
128     case GDK_VISUAL_STATIC_COLOR:
129     case GDK_VISUAL_GRAYSCALE:
130     case GDK_VISUAL_PSEUDO_COLOR:
131       private->info = g_new0 (GdkColorInfo, colormap->size);
132       colormap->colors = g_new (GdkColor, colormap->size);
133       
134       private->hash = g_hash_table_new ((GHashFunc) gdk_color_hash,
135                                         (GCompareFunc) gdk_color_equal);
136       
137       if (private_cmap)
138         {
139           guint16 red[256], green[256], blue[256];
140           struct fb_cmap fbc = {0, 256};
141
142           fbc.red = red;
143           fbc.green = green;
144           fbc.blue = blue;
145
146           if(ioctl(fbd->fd, FBIOGETCMAP, &fbc))
147             g_error("ioctl(FBIOGETCMAP) failed");
148
149           for (i = 0; i < colormap->size; i++)
150             {
151               colormap->colors[i].pixel = i;
152               colormap->colors[i].red = red[i];
153               colormap->colors[i].green = green[i];
154               colormap->colors[i].blue = blue[i];
155             }
156
157           gdk_colormap_change (colormap, colormap->size);
158         }
159       break;
160
161     case GDK_VISUAL_DIRECT_COLOR:
162       g_error("NYI");
163 #if 0
164       colormap->colors = g_new (GdkColor, colormap->size);
165
166       size = 1 << visual->red_prec;
167       for (i = 0; i < size; i++)
168         colormap->colors[i].red = i * 65535 / (size - 1);
169
170       size = 1 << visual->green_prec;
171       for (i = 0; i < size; i++)
172         colormap->colors[i].green = i * 65535 / (size - 1);
173
174       size = 1 << visual->blue_prec;
175       for (i = 0; i < size; i++)
176         colormap->colors[i].blue = i * 65535 / (size - 1);
177
178       gdk_colormap_change (colormap, colormap->size);
179 #endif
180       break;
181
182     default:
183       g_assert_not_reached();
184
185     case GDK_VISUAL_TRUE_COLOR:
186       break;
187     }
188
189   return colormap;
190 }
191
192 #define MIN_SYNC_TIME 2
193
194 void
195 gdk_colormap_sync (GdkColormap *colormap,
196                    gboolean     force)
197 {
198   
199 }                  
200
201 GdkColormap*
202 gdk_colormap_get_system (void)
203 {
204   static GdkColormap *colormap = NULL;
205
206   if (!colormap)
207     {
208       guint16 red[256], green[256], blue[256];
209       struct fb_cmap fbc = {0, 256};
210       int i, r, g, b;
211       GdkVisual *visual = gdk_visual_get_system();
212
213       if(visual->type == GDK_VISUAL_GRAYSCALE
214          || visual->type ==  GDK_VISUAL_PSEUDO_COLOR)
215         {
216           fbc.red = red;
217           fbc.green = green;
218           fbc.blue = blue;
219           switch(visual->type)
220             {
221             case GDK_VISUAL_GRAYSCALE:
222               for(i = 0; i < 256; i++)
223                 red[i] = green[i] = blue[i] = i << 8;
224               i--;
225               red[i] = green[i] = blue[i] = 65535; /* Make it a true white */
226               break;
227             case GDK_VISUAL_PSEUDO_COLOR:
228               /* Color cube stolen from gdkrgb upon advice from Owen */
229               for(i = r = 0; r < 6; r++)
230                 for(g = 0; g < 6; g++)
231                   for(b = 0; b < 6; b++)
232                     {
233                       red[i] = r * 65535 / 5;
234                       green[i] = g * 65535 / 5;
235                       blue[i] = b * 65535 / 5;
236                       i++;
237                     }
238               g_assert(i == 216);
239
240           /* Fill in remaining space with grays */
241               for(i = 216; i < 256; i++)
242                 {
243                   red[i] = green[i] = blue[i] =
244                     (i - 216) * 40;
245                 }
246               /* Real white */
247               red[255] = green[255] = blue[255] = 65535;
248               break;
249             default:
250               break;
251             }
252
253           ioctl(gdk_display->fd, FBIOPUTCMAP, &fbc);
254         }
255
256       colormap = gdk_colormap_new(visual, TRUE);
257     }
258
259   return colormap;
260 }
261
262 gint
263 gdk_colormap_get_system_size (void)
264 {
265   return 1 << (gdk_display->modeinfo.bits_per_pixel);
266 }
267
268 void
269 gdk_colormap_change (GdkColormap *colormap,
270                      gint         ncolors)
271 {
272   guint16 red[256], green[256], blue[256];
273   struct fb_cmap fbc = {0,256};
274   GdkColormapPrivateFB *private;
275   int i;
276
277   g_return_if_fail (colormap != NULL);
278
279   fbc.red = red;
280   fbc.green = green;
281   fbc.blue = blue;
282
283   private = (GdkColormapPrivateFB*) colormap;
284   switch (private->base.visual->type)
285     {
286     case GDK_VISUAL_GRAYSCALE:
287       for(i = 0; i < ncolors; i++)
288         {
289           red[i] = green[i] = blue[i] =
290             (colormap->colors[i].red +
291              colormap->colors[i].green +
292              colormap->colors[i].blue)/3;
293         }
294       ioctl(gdk_display->fd, FBIOPUTCMAP, &fbc);
295       break;
296
297     case GDK_VISUAL_PSEUDO_COLOR:
298       for (i = 0; i < ncolors; i++)
299         {
300           red[i] = colormap->colors[i].red;
301           green[i] = colormap->colors[i].green;
302           blue[i] = colormap->colors[i].blue;
303         }
304       ioctl(gdk_display->fd, FBIOPUTCMAP, &fbc);
305       break;
306
307     default:
308       break;
309     }
310 }
311
312 gboolean
313 gdk_color_parse (const gchar *spec,
314                  GdkColor *color)
315 {
316   char aline[512];
317   FILE *fh;
318
319   g_return_val_if_fail(spec, FALSE);
320   g_return_val_if_fail(color, FALSE);
321
322   if(spec[0] == '#')
323     {
324       if(strlen(spec) == 7)
325         {
326           guint num;
327
328           sscanf(spec + 1, "%x", &num);
329           color->red = (num & 0xFF0000) >> 8;
330           color->green = (num & 0xFF00);
331           color->blue = (num & 0xFF) << 8;
332         }
333       else if(strlen(spec) == 13)
334         {
335           char s1[5], s2[5], s3[5];
336           g_snprintf(s1, sizeof(s1), spec + 1);
337           g_snprintf(s2, sizeof(s2), spec + 5);
338           g_snprintf(s3, sizeof(s3), spec + 9);
339
340           if(!sscanf(s1, "%hx", &color->red))
341             g_error("sscanf failed");
342           if(!sscanf(s2, "%hx", &color->green))
343             g_error("sscanf failed");
344           if(!sscanf(s3, "%hx", &color->blue))
345             g_error("sscanf failed");
346         }
347       else
348         {
349           g_warning("Couldn't parse color specifier `%s'", spec);
350           return FALSE;
351         }
352
353       return TRUE;
354     }
355   else
356     {
357       fh = fopen("/usr/lib/X11/rgb.txt", "r");
358       if(!fh)
359         return FALSE;
360
361       while(fgets(aline, sizeof(aline), fh))
362         {
363           int red, green, blue;
364           char *ctmp;
365
366           g_strstrip(aline);
367           if(!aline[0] || aline[0] == '#' || aline[0] == '!')
368             continue;
369
370           ctmp = strtok(aline, " \t");
371           if(!ctmp)
372             continue;
373           red = atoi(ctmp);
374
375           ctmp = strtok(NULL, " \t");
376           if(!ctmp)
377             continue;
378           green = atoi(ctmp);
379
380           ctmp = strtok(NULL, " \t");
381           if(!ctmp)
382             continue;
383           blue = atoi(ctmp);
384
385           ctmp = strtok(NULL, " \t");
386           if(!ctmp || strcmp(ctmp, spec))
387             continue;
388
389           color->red = red << 8;
390           color->green = green << 8;
391           color->blue = blue << 8;
392           return TRUE;
393         }
394       fclose(fh);
395     }
396
397   return FALSE;
398 }
399
400 void
401 gdk_colormap_free_colors (GdkColormap *colormap,
402                           GdkColor    *colors,
403                           gint         ncolors)
404 {
405   GdkColormapPrivateFB *private;
406   gint i;
407
408   g_return_if_fail (colormap != NULL);
409   g_return_if_fail (colors != NULL);
410
411   private = (GdkColormapPrivateFB*) colormap;
412
413   if ((private->base.visual->type != GDK_VISUAL_PSEUDO_COLOR) &&
414       (private->base.visual->type != GDK_VISUAL_GRAYSCALE))
415     return;
416
417   for (i=0; i<ncolors; i++)
418     {
419       gulong pixel = colors[i].pixel;
420       
421       if (private->info[pixel].ref_count)
422         {
423           private->info[pixel].ref_count--;
424
425           if (private->info[pixel].ref_count == 0)
426             {
427               if (!(private->info[pixel].flags & GDK_COLOR_WRITEABLE))
428                 g_hash_table_remove (private->hash, &colormap->colors[pixel]);
429               private->info[pixel].flags = 0;
430             }
431         }
432     }
433 }
434
435 /********************
436  * Color allocation *
437  ********************/
438
439 /* Try to allocate a single color using XAllocColor. If it succeeds,
440  * cache the result in our colormap, and store in ret.
441  */
442 static gboolean 
443 gdk_colormap_alloc1 (GdkColormap *colormap,
444                      GdkColor    *color,
445                      GdkColor    *ret)
446 {
447   GdkColormapPrivateFB *private;
448   int i;
449
450   private = (GdkColormapPrivateFB*) colormap;
451
452   if(private->base.visual->type != GDK_VISUAL_GRAYSCALE
453      && private->base.visual->type != GDK_VISUAL_PSEUDO_COLOR)
454     return FALSE;
455
456   *ret = *color;
457   if(!color->red && !color->green && !color->blue) /* black */
458     {
459       ret->pixel = 0;
460       private->info[ret->pixel].ref_count++;
461       return TRUE;
462     }
463
464   if(color->red == 65535 && color->green == 65535 && color->blue == 65535) /* white */
465     {
466       ret->pixel = 255;
467       private->info[ret->pixel].ref_count++;
468       return TRUE;
469     }
470
471   for(i = 1; i < (colormap->size - 1); i++)
472     {
473       if(!private->info[i].ref_count)
474         {
475           guint16 red = color->red, green = color->green, blue = color->blue;
476           struct fb_cmap fbc;
477           fbc.len = 1;
478           fbc.start = i;
479           fbc.red = &red;
480           fbc.green = &green;
481           fbc.blue = &blue;
482
483           ioctl(gdk_display->fd, FBIOPUTCMAP, &fbc);
484
485           ret->pixel = i;
486           colormap->colors[ret->pixel] = *ret;
487           private->info[ret->pixel].ref_count = 1;
488           g_hash_table_insert (private->hash,
489                                &colormap->colors[ret->pixel],
490                                &colormap->colors[ret->pixel]);
491           return TRUE;
492         }
493     }
494
495   return FALSE;
496 }
497
498 static gint
499 gdk_colormap_alloc_colors_shared (GdkColormap *colormap,
500                                   GdkColor    *colors,
501                                   gint         ncolors,
502                                   gboolean     writeable,
503                                   gboolean     best_match,
504                                   gboolean    *success)
505 {
506   GdkColormapPrivateFB *private;
507   gint i, index;
508   gint nremaining = 0;
509   gint nfailed = 0;
510
511   private = (GdkColormapPrivateFB*) colormap;
512   index = -1;
513
514   for (i=0; i<ncolors; i++)
515     {
516       if (!success[i])
517         {
518           if (gdk_colormap_alloc1 (colormap, &colors[i], &colors[i]))
519             success[i] = TRUE;
520           else
521             nremaining++;
522         }
523     }
524
525
526   if (nremaining > 0 && best_match)
527     {
528       gchar *available = g_new (gchar, colormap->size);
529
530       for (i = 0; i < colormap->size; i++)
531         available[i] = ((private->info[i].ref_count == 0) ||
532                         !(private->info[i].flags && GDK_COLOR_WRITEABLE));
533       
534       while (nremaining > 0)
535         {
536           for (i=0; i<ncolors; i++)
537             {
538               if (!success[i])
539                 {
540                   index = gdk_colormap_match_color (colormap, &colors[i], available);
541                   if (index != -1)
542                     {
543                       if (private->info[index].ref_count)
544                         {
545                           private->info[index].ref_count++;
546                           colors[i] = colormap->colors[index];
547                           success[i] = TRUE;
548                           nremaining--;
549                         }
550                       else
551                         {
552                           if (gdk_colormap_alloc1 (colormap, 
553                                                    &colormap->colors[index],
554                                                    &colors[i]))
555                             {
556                               success[i] = TRUE;
557                               nremaining--;
558                               break;
559                             }
560                           else
561                             {
562                               available[index] = FALSE;
563                             }
564                         }
565                     }
566                   else
567                     {
568                       nfailed++;
569                       nremaining--;
570                       success[i] = 2; /* flag as permanent failure */
571                     }
572                 }
573             }
574         }
575       g_free (available);
576     }
577
578   /* Change back the values we flagged as permanent failures */
579   if (nfailed > 0)
580     {
581       for (i=0; i<ncolors; i++)
582         if (success[i] == 2)
583           success[i] = FALSE;
584       nremaining = nfailed;
585     }
586   
587   return (ncolors - nremaining);
588 }
589
590 static gint
591 gdk_colormap_alloc_colors_pseudocolor (GdkColormap *colormap,
592                                        GdkColor    *colors,
593                                        gint         ncolors,
594                                        gboolean     writeable,
595                                        gboolean     best_match,
596                                        gboolean    *success)
597 {
598   GdkColormapPrivateFB *private;
599   GdkColor *lookup_color;
600   gint i;
601   gint nremaining = 0;
602
603   private = (GdkColormapPrivateFB*) colormap;
604
605   /* Check for an exact match among previously allocated colors */
606
607   for (i=0; i<ncolors; i++)
608     {
609       if (!success[i])
610         {
611           lookup_color = g_hash_table_lookup (private->hash, &colors[i]);
612           if (lookup_color)
613             {
614               private->info[lookup_color->pixel].ref_count++;
615               colors[i].pixel = lookup_color->pixel;
616               success[i] = TRUE;
617             }
618           else
619             nremaining++;
620         }
621     }
622
623   /* If that failed, we try to allocate a new color, or approxmiate
624    * with what we can get if best_match is TRUE.
625    */
626   if (nremaining > 0)
627     return gdk_colormap_alloc_colors_shared (colormap, colors, ncolors, writeable, best_match, success);
628   else
629     return 0;
630 }
631
632 gint
633 gdk_colormap_alloc_colors (GdkColormap *colormap,
634                            GdkColor    *colors,
635                            gint         ncolors,
636                            gboolean     writeable,
637                            gboolean     best_match,
638                            gboolean    *success)
639 {
640   GdkColormapPrivateFB *private;
641   GdkVisual *visual;
642   gint i;
643   gint nremaining = 0;
644
645   g_return_val_if_fail (colormap != NULL, FALSE);
646   g_return_val_if_fail (colors != NULL, FALSE);
647
648   private = (GdkColormapPrivateFB*) colormap;
649
650   for (i=0; i<ncolors; i++)
651     success[i] = FALSE;
652
653   switch (private->base.visual->type)
654     {
655     case GDK_VISUAL_PSEUDO_COLOR:
656     case GDK_VISUAL_GRAYSCALE:
657     case GDK_VISUAL_STATIC_GRAY:
658     case GDK_VISUAL_STATIC_COLOR:
659       return gdk_colormap_alloc_colors_pseudocolor (colormap, colors, ncolors,
660                                                     writeable, best_match, success);
661       break;
662
663     case GDK_VISUAL_DIRECT_COLOR:
664     case GDK_VISUAL_TRUE_COLOR:
665       visual = private->base.visual;
666
667       for (i=0; i<ncolors; i++)
668         {
669           colors[i].pixel = (((colors[i].red >> (16 - visual->red_prec)) << visual->red_shift) +
670                              ((colors[i].green >> (16 - visual->green_prec)) << visual->green_shift) +
671                              ((colors[i].blue >> (16 - visual->blue_prec)) << visual->blue_shift));
672           success[i] = TRUE;
673         }
674       break;
675     }
676   return nremaining;
677 }
678
679 gboolean
680 gdk_color_change (GdkColormap *colormap,
681                   GdkColor    *color)
682 {
683   GdkColormapPrivateFB *private;
684   struct fb_cmap fbc = {0, 1};
685
686   g_return_val_if_fail (colormap != NULL, FALSE);
687   g_return_val_if_fail (color != NULL, FALSE);
688
689   private = (GdkColormapPrivateFB*) colormap;
690
691   switch(private->base.visual->type)
692     {
693     case GDK_VISUAL_GRAYSCALE:
694       color->red = color->green = color->blue = (color->red + color->green + color->blue)/3;
695
696     case GDK_VISUAL_PSEUDO_COLOR:
697       fbc.start = color->pixel;
698       fbc.red = &color->red;
699       fbc.green = &color->green;
700       fbc.blue = &color->blue;
701       ioctl(gdk_display->fd, FBIOPUTCMAP, &fbc);
702       break;
703
704     default:
705       break;
706     }
707
708   return TRUE;
709 }
710
711 static gint
712 gdk_colormap_match_color (GdkColormap *cmap,
713                           GdkColor    *color,
714                           const gchar *available)
715 {
716   GdkColor *colors;
717   guint sum, max;
718   gint rdiff, gdiff, bdiff;
719   gint i, index;
720
721   g_return_val_if_fail (cmap != NULL, 0);
722   g_return_val_if_fail (color != NULL, 0);
723
724   colors = cmap->colors;
725   max = 3 * (65536);
726   index = -1;
727
728   for (i = 0; i < cmap->size; i++)
729     {
730       if ((!available) || (available && available[i]))
731         {
732           rdiff = (color->red - colors[i].red);
733           gdiff = (color->green - colors[i].green);
734           bdiff = (color->blue - colors[i].blue);
735
736           sum = ABS (rdiff) + ABS (gdiff) + ABS (bdiff);
737
738           if (sum < max)
739             {
740               index = i;
741               max = sum;
742             }
743         }
744     }
745
746   return index;
747 }
748
749 gint gdk_colors_alloc    (GdkColormap   *colormap,
750                           gboolean       contiguous,
751                           gulong        *planes,
752                           gint           nplanes,
753                           gulong        *pixels,
754                           gint           npixels)
755 {
756   return 0;
757 }
758
759 void
760 gdk_colors_free  (GdkColormap   *colormap,
761                   gulong        *pixels,
762                   gint           npixels,
763                   gulong         planes)
764 {
765 }