]> Pileus Git - ~andy/gtk/blob - gdk/x11/gdkscreen-x11.c
3ab2bfc8afa238a5182c2667c688f81dc7d610f8
[~andy/gtk] / gdk / x11 / gdkscreen-x11.c
1 /*
2  * gdkscreen-x11.c
3  * 
4  * Copyright 2001 Sun Microsystems Inc. 
5  *
6  * Erwann Chenede <erwann.chenede@sun.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public
19  * License along with this library; if not, write to the
20  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  */
23
24 #include <config.h>
25
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <glib.h>
30 #include "gdkscreen.h"
31 #include "gdkscreen-x11.h"
32 #include "gdkdisplay.h"
33 #include "gdkdisplay-x11.h"
34 #include "gdkx.h"
35 #include "gdkalias.h"
36
37 #ifdef HAVE_SOLARIS_XINERAMA
38 #include <X11/extensions/xinerama.h>
39 #endif
40 #ifdef HAVE_XFREE_XINERAMA
41 #include <X11/extensions/Xinerama.h>
42 #endif
43
44 #ifdef HAVE_RANDR
45 #include <X11/extensions/Xrandr.h>
46 #endif
47
48 static void         gdk_screen_x11_class_init  (GdkScreenX11Class *klass);
49 static void         gdk_screen_x11_dispose     (GObject           *object);
50 static void         gdk_screen_x11_finalize    (GObject           *object);
51 static void         init_xinerama_support      (GdkScreen         *screen);
52 static void         init_randr_support         (GdkScreen         *screen);
53
54 enum
55 {
56   WINDOW_MANAGER_CHANGED,
57   LAST_SIGNAL
58 };
59
60 static gpointer parent_class = NULL;
61 static guint signals[LAST_SIGNAL] = { 0 };
62
63 GType
64 _gdk_screen_x11_get_type (void)
65 {
66   static GType object_type = 0;
67
68   if (!object_type)
69     {
70       static const GTypeInfo object_info =
71         {
72           sizeof (GdkScreenX11Class),
73           (GBaseInitFunc) NULL,
74           (GBaseFinalizeFunc) NULL,
75           (GClassInitFunc) gdk_screen_x11_class_init,
76           NULL,                 /* class_finalize */
77           NULL,                 /* class_data */
78           sizeof (GdkScreenX11),
79           0,                    /* n_preallocs */
80           (GInstanceInitFunc) NULL,
81         };
82       object_type = g_type_register_static (GDK_TYPE_SCREEN,
83                                             "GdkScreenX11",
84                                             &object_info, 0);
85     }
86   return object_type;
87 }
88
89 static void
90 gdk_screen_x11_class_init (GdkScreenX11Class *klass)
91 {
92   GObjectClass *object_class = G_OBJECT_CLASS (klass);
93   
94   object_class->dispose = gdk_screen_x11_dispose;
95   object_class->finalize = gdk_screen_x11_finalize;
96
97   parent_class = g_type_class_peek_parent (klass);
98
99   signals[WINDOW_MANAGER_CHANGED] =
100     g_signal_new ("window_manager_changed",
101                   G_OBJECT_CLASS_TYPE (object_class),
102                   G_SIGNAL_RUN_LAST,
103                   G_STRUCT_OFFSET (GdkScreenX11Class, window_manager_changed),
104                   NULL, NULL,
105                   g_cclosure_marshal_VOID__VOID,
106                   G_TYPE_NONE,
107                   0);
108 }
109
110 /**
111  * gdk_screen_get_display:
112  * @screen: a #GdkScreen
113  *
114  * Gets the display to which the @screen belongs.
115  * 
116  * Returns: the display to which @screen belongs
117  *
118  * Since: 2.2
119  **/
120 GdkDisplay *
121 gdk_screen_get_display (GdkScreen *screen)
122 {
123   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
124
125   return GDK_SCREEN_X11 (screen)->display;
126 }
127 /**
128  * gdk_screen_get_width:
129  * @screen: a #GdkScreen
130  *
131  * Gets the width of @screen in pixels
132  * 
133  * Returns: the width of @screen in pixels.
134  *
135  * Since: 2.2
136  **/
137 gint
138 gdk_screen_get_width (GdkScreen *screen)
139 {
140   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
141
142   return WidthOfScreen (GDK_SCREEN_X11 (screen)->xscreen);
143 }
144
145 /**
146  * gdk_screen_get_height:
147  * @screen: a #GdkScreen
148  *
149  * Gets the height of @screen in pixels
150  * 
151  * Returns: the height of @screen in pixels.
152  *
153  * Since: 2.2
154  **/
155 gint
156 gdk_screen_get_height (GdkScreen *screen)
157 {
158   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
159
160   return HeightOfScreen (GDK_SCREEN_X11 (screen)->xscreen);
161 }
162
163 /**
164  * gdk_screen_get_width_mm:
165  * @screen: a #GdkScreen
166  *
167  * Gets the width of @screen in millimeters. 
168  * Note that on some X servers this value will not be correct.
169  * 
170  * Returns: the width of @screen in millimeters.
171  *
172  * Since: 2.2
173  **/
174 gint
175 gdk_screen_get_width_mm (GdkScreen *screen)
176 {
177   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);  
178
179   return WidthMMOfScreen (GDK_SCREEN_X11 (screen)->xscreen);
180 }
181
182 /**
183  * gdk_screen_get_height_mm:
184  * @screen: a #GdkScreen
185  *
186  * Returns the height of @screen in millimeters. 
187  * Note that on some X servers this value will not be correct.
188  * 
189  * Returns: the heigth of @screen in millimeters.
190  *
191  * Since: 2.2
192  **/
193 gint
194 gdk_screen_get_height_mm (GdkScreen *screen)
195 {
196   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
197
198   return HeightMMOfScreen (GDK_SCREEN_X11 (screen)->xscreen);
199 }
200
201 /**
202  * gdk_screen_get_number:
203  * @screen: a #GdkScreen
204  *
205  * Gets the index of @screen among the screens in the display
206  * to which it belongs. (See gdk_screen_get_display())
207  * 
208  * Returns: the index
209  *
210  * Since: 2.2
211  **/
212 gint
213 gdk_screen_get_number (GdkScreen *screen)
214 {
215   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);  
216   
217   return GDK_SCREEN_X11 (screen)->screen_num;
218 }
219
220 /**
221  * gdk_screen_get_root_window:
222  * @screen: a #GdkScreen
223  *
224  * Gets the root window of @screen. 
225  * 
226  * Returns: the root window
227  *
228  * Since: 2.2
229  **/
230 GdkWindow *
231 gdk_screen_get_root_window (GdkScreen *screen)
232 {
233   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
234
235   return GDK_SCREEN_X11 (screen)->root_window;
236 }
237
238 /**
239  * gdk_screen_get_default_colormap:
240  * @screen: a #GdkScreen
241  *
242  * Gets the default colormap for @screen.
243  * 
244  * Returns: the default #GdkColormap.
245  *
246  * Since: 2.2
247  **/
248 GdkColormap *
249 gdk_screen_get_default_colormap (GdkScreen *screen)
250 {
251   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
252
253   return GDK_SCREEN_X11 (screen)->default_colormap;
254 }
255
256 /**
257  * gdk_screen_set_default_colormap:
258  * @screen: a #GdkScreen
259  * @colormap: a #GdkColormap
260  *
261  * Sets the default @colormap for @screen.
262  *
263  * Since: 2.2
264  **/
265 void
266 gdk_screen_set_default_colormap (GdkScreen   *screen,
267                                  GdkColormap *colormap)
268 {
269   GdkColormap *old_colormap;
270   
271   g_return_if_fail (GDK_IS_SCREEN (screen));
272   g_return_if_fail (GDK_IS_COLORMAP (colormap));
273
274   old_colormap = GDK_SCREEN_X11 (screen)->default_colormap;
275
276   GDK_SCREEN_X11 (screen)->default_colormap = g_object_ref (colormap);
277   
278   if (old_colormap)
279     g_object_unref (old_colormap);
280 }
281
282 static void
283 gdk_screen_x11_dispose (GObject *object)
284 {
285   GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (object);
286
287   _gdk_x11_events_uninit_screen (GDK_SCREEN (object));
288
289   g_object_unref (screen_x11->default_colormap);
290   screen_x11->default_colormap = NULL;
291   
292   screen_x11->root_window = NULL;
293
294   screen_x11->xdisplay = NULL;
295   screen_x11->xscreen = NULL;
296   screen_x11->screen_num = -1;
297   screen_x11->xroot_window = None;
298   screen_x11->wmspec_check_window = None;
299   
300   G_OBJECT_CLASS (parent_class)->dispose (object);
301 }
302
303 static void
304 gdk_screen_x11_finalize (GObject *object)
305 {
306   GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (object);
307   /* int i; */
308   g_object_unref (screen_x11->root_window);
309
310   if (screen_x11->renderer)
311     g_object_unref (screen_x11->renderer);
312   
313   /* Visual Part (Need to implement finalize for Visuals for a clean
314    * finalize) */
315   /* for (i=0;i<screen_x11->nvisuals;i++)
316     g_object_unref (screen_x11->visuals[i]);*/
317   g_free (screen_x11->visuals);
318   g_hash_table_destroy (screen_x11->visual_hash);
319
320   g_free (screen_x11->window_manager_name);  
321
322   g_hash_table_destroy (screen_x11->colormap_hash);
323   /* X settings */
324   g_free (screen_x11->xsettings_client);
325   g_free (screen_x11->monitors);
326   
327   G_OBJECT_CLASS (parent_class)->finalize (object);
328 }
329
330 /**
331  * gdk_screen_get_n_monitors:
332  * @screen: a #GdkScreen.
333  *
334  * Returns the number of monitors which @screen consists of.
335  *
336  * Returns: number of monitors which @screen consists of.
337  *
338  * Since: 2.2
339  **/
340 gint 
341 gdk_screen_get_n_monitors (GdkScreen *screen)
342 {
343   g_return_val_if_fail (GDK_IS_SCREEN (screen), 0);
344   
345   return GDK_SCREEN_X11 (screen)->num_monitors;
346 }
347
348 /**
349  * gdk_screen_get_monitor_geometry:
350  * @screen : a #GdkScreen.
351  * @monitor_num: the monitor number. 
352  * @dest : a #GdkRectangle to be filled with the monitor geometry
353  *
354  * Retrieves the #GdkRectangle representing the size and position of 
355  * the individual monitor within the entire screen area.
356  * 
357  * Note that the size of the entire screen area can be retrieved via 
358  * gdk_screen_get_width() and gdk_screen_get_height().
359  *
360  * Since: 2.2
361  **/
362 void 
363 gdk_screen_get_monitor_geometry (GdkScreen    *screen,
364                                  gint          monitor_num,
365                                  GdkRectangle *dest)
366 {
367   g_return_if_fail (GDK_IS_SCREEN (screen));
368   g_return_if_fail (monitor_num < GDK_SCREEN_X11 (screen)->num_monitors);
369   g_return_if_fail (monitor_num >= 0);
370
371   *dest = GDK_SCREEN_X11 (screen)->monitors[monitor_num];
372 }
373
374 /**
375  * gdk_x11_screen_get_xscreen:
376  * @screen: a #GdkScreen.
377  * @returns: an Xlib <type>Screen*</type>
378  *
379  * Returns the screen of a #GdkScreen.
380  *
381  * Since: 2.2
382  */
383 Screen *
384 gdk_x11_screen_get_xscreen (GdkScreen *screen)
385 {
386   return GDK_SCREEN_X11 (screen)->xscreen;
387 }
388
389
390 /**
391  * gdk_x11_screen_get_screen_number:
392  * @screen: a #GdkScreen.
393  * @returns: the position of @screen among the screens of
394  *   its display.
395  *
396  * Returns the index of a #GdkScreen.
397  *
398  * Since: 2.2
399  */
400 int
401 gdk_x11_screen_get_screen_number (GdkScreen *screen)
402 {
403   return GDK_SCREEN_X11 (screen)->screen_num;
404 }
405
406 GdkScreen *
407 _gdk_x11_screen_new (GdkDisplay *display,
408                      gint        screen_number) 
409 {
410   GdkScreen *screen;
411   GdkScreenX11 *screen_x11;
412   GdkDisplayX11 *display_x11 = GDK_DISPLAY_X11 (display);
413
414   screen = g_object_new (GDK_TYPE_SCREEN_X11, NULL);
415
416   screen_x11 = GDK_SCREEN_X11 (screen);
417   screen_x11->display = display;
418   screen_x11->xdisplay = display_x11->xdisplay;
419   screen_x11->xscreen = ScreenOfDisplay (display_x11->xdisplay, screen_number);
420   screen_x11->screen_num = screen_number;
421   screen_x11->xroot_window = RootWindow (display_x11->xdisplay,screen_number);
422   screen_x11->wmspec_check_window = None;
423   /* we want this to be always non-null */
424   screen_x11->window_manager_name = g_strdup ("unknown");
425   
426   init_xinerama_support (screen);
427   init_randr_support (screen);
428   
429   _gdk_visual_init (screen);
430   _gdk_windowing_window_init (screen);
431
432   return screen;
433 }
434
435 #ifdef HAVE_XINERAMA
436 static gboolean
437 check_solaris_xinerama (GdkScreen *screen)
438 {
439 #ifdef HAVE_SOLARIS_XINERAMA
440   
441   if (XineramaGetState (GDK_SCREEN_XDISPLAY (screen),
442                         gdk_screen_get_number (screen)))
443     {
444       XRectangle monitors[MAXFRAMEBUFFERS];
445       unsigned char hints[16];
446       gint result;
447       GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
448
449       result = XineramaGetInfo (GDK_SCREEN_XDISPLAY (screen),
450                                 gdk_screen_get_number (screen),
451                                 monitors, hints,
452                                 &screen_x11->num_monitors);
453       /* Yes I know it should be Success but the current implementation 
454           returns the num of monitor*/
455       if (result == 0)
456         {
457           /* FIXME: We need to trap errors, since XINERAMA isn't always XINERAMA.
458            */ 
459           g_error ("error while retrieving Xinerama information");
460         }
461       else
462         {
463           int i;
464           screen_x11->monitors = g_new0 (GdkRectangle, screen_x11->num_monitors);
465           
466           for (i = 0; i < screen_x11->num_monitors; i++)
467             {
468               screen_x11->monitors[i].x = monitors[i].x;
469               screen_x11->monitors[i].y = monitors[i].y;
470               screen_x11->monitors[i].width = monitors[i].width;
471               screen_x11->monitors[i].height = monitors[i].height;
472             }
473
474           return TRUE;
475         }
476     }
477 #endif /* HAVE_SOLARIS_XINERAMA */
478   
479   return FALSE;
480 }
481
482 static gboolean
483 check_xfree_xinerama (GdkScreen *screen)
484 {
485 #ifdef HAVE_XFREE_XINERAMA
486   if (XineramaIsActive (GDK_SCREEN_XDISPLAY (screen)))
487     {
488       GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
489       XineramaScreenInfo *monitors = XineramaQueryScreens (GDK_SCREEN_XDISPLAY (screen),
490                                                            &screen_x11->num_monitors);
491       if (screen_x11->num_monitors <= 0)
492         {
493           /* FIXME: We need to trap errors, since XINERAMA isn't always XINERAMA.
494            *        I don't think the num_monitors <= 0 check has any validity.
495            */ 
496           g_error ("error while retrieving Xinerama information");
497         }
498       else
499         {
500           int i;
501           screen_x11->monitors = g_new0 (GdkRectangle, screen_x11->num_monitors);
502           
503           for (i = 0; i < screen_x11->num_monitors; i++)
504             {
505               screen_x11->monitors[i].x = monitors[i].x_org;
506               screen_x11->monitors[i].y = monitors[i].y_org;
507               screen_x11->monitors[i].width = monitors[i].width;
508               screen_x11->monitors[i].height = monitors[i].height;
509             }
510
511           XFree (monitors);
512
513           return TRUE;
514         }
515     }
516 #endif /* HAVE_XFREE_XINERAMA */
517   
518   return FALSE;
519 }
520 #endif /* HAVE_XINERAMA */
521
522 static void
523 init_xinerama_support (GdkScreen * screen)
524 {
525   GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
526 #ifdef HAVE_XINERAMA
527   int opcode, firstevent, firsterror;
528 #endif
529
530   if (screen_x11->monitors)
531     g_free (screen_x11->monitors);
532   
533 #ifdef HAVE_XINERAMA  
534   if (XQueryExtension (GDK_SCREEN_XDISPLAY (screen), "XINERAMA",
535                        &opcode, &firstevent, &firsterror))
536     {
537       if (check_solaris_xinerama (screen) ||
538           check_xfree_xinerama (screen))
539         return;
540     }
541 #endif /* HAVE_XINERAMA */
542
543   /* No Xinerama
544    */
545 #ifdef G_ENABLE_DEBUG
546   if (_gdk_debug_flags & GDK_DEBUG_XINERAMA)
547     {
548       /* Fake Xinerama mode by splitting the screen into 4 monitors.
549        * Also draw a little cross to make the monitor boundaries visible.
550        */
551       XSetWindowAttributes atts;
552       Window win;
553       gint w, h;
554
555       w = WidthOfScreen (screen_x11->xscreen);
556       h = HeightOfScreen (screen_x11->xscreen);
557       screen_x11->num_monitors = 4;
558       screen_x11->monitors = g_new0 (GdkRectangle, 4);
559       screen_x11->monitors[0].x = 0;
560       screen_x11->monitors[0].y = 0;
561       screen_x11->monitors[0].width = w / 2;
562       screen_x11->monitors[0].height = h / 2;
563       screen_x11->monitors[1].x = w / 2;
564       screen_x11->monitors[1].y = 0;
565       screen_x11->monitors[1].width = w / 2;
566       screen_x11->monitors[1].height = h / 2;
567       screen_x11->monitors[2].x = 0;
568       screen_x11->monitors[2].y = h / 2;
569       screen_x11->monitors[2].width = w / 2;
570       screen_x11->monitors[2].height = h / 2;
571       screen_x11->monitors[3].x = w / 2;
572       screen_x11->monitors[3].y = h / 2;
573       screen_x11->monitors[3].width = w / 2;
574       screen_x11->monitors[3].height = h / 2;
575       atts.override_redirect = 1;
576       atts.background_pixel = WhitePixel(GDK_SCREEN_XDISPLAY (screen), 
577                                          screen_x11->screen_num);
578       win = XCreateWindow(GDK_SCREEN_XDISPLAY (screen), 
579                           screen_x11->xroot_window, 0, h / 2, w, 1, 0, 
580                           DefaultDepth(GDK_SCREEN_XDISPLAY (screen), 
581                                        screen_x11->screen_num),
582                           InputOutput, 
583                           DefaultVisual(GDK_SCREEN_XDISPLAY (screen), 
584                                         screen_x11->screen_num),
585                           CWOverrideRedirect|CWBackPixel, 
586                           &atts);
587       XMapRaised(GDK_SCREEN_XDISPLAY (screen), win); 
588       win = XCreateWindow(GDK_SCREEN_XDISPLAY (screen), 
589                           screen_x11->xroot_window, w/2 , 0, 1, h, 0, 
590                           DefaultDepth(GDK_SCREEN_XDISPLAY (screen), 
591                                        screen_x11->screen_num),
592                           InputOutput, 
593                           DefaultVisual(GDK_SCREEN_XDISPLAY (screen), 
594                                         screen_x11->screen_num),
595                           CWOverrideRedirect|CWBackPixel, 
596                           &atts);
597       XMapRaised(GDK_SCREEN_XDISPLAY (screen), win); 
598     }
599   else
600 #endif
601     {
602        screen_x11->num_monitors = 1;
603        screen_x11->monitors = g_new0 (GdkRectangle, 1);
604        screen_x11->monitors[0].x = 0;
605        screen_x11->monitors[0].y = 0;
606        screen_x11->monitors[0].width = WidthOfScreen (screen_x11->xscreen);
607        screen_x11->monitors[0].height = HeightOfScreen (screen_x11->xscreen);
608     }
609 }
610
611 static void
612 init_randr_support (GdkScreen * screen)
613 {
614   GdkScreenX11 *screen_x11 = GDK_SCREEN_X11 (screen);
615   
616   XSelectInput (GDK_SCREEN_XDISPLAY (screen),
617                 screen_x11->xroot_window,
618                 StructureNotifyMask);
619 }
620
621 void
622 _gdk_x11_screen_size_changed (GdkScreen *screen,
623                               XEvent    *event)
624 {
625 #ifdef HAVE_RANDR
626   if (!XRRUpdateConfiguration (event))
627     return;
628 #else
629   if (event->type == ConfigureNotify)
630     {
631       XConfigureEvent *rcevent = (XConfigureEvent *) event;
632       Screen        *xscreen = gdk_x11_screen_get_xscreen (screen);
633       
634       xscreen->width   = rcevent->width;
635       xscreen->height  = rcevent->height;
636     }
637   else
638     return;
639 #endif
640   
641   init_xinerama_support (screen);
642   g_signal_emit_by_name (screen, "size_changed");
643 }
644
645 void
646 _gdk_x11_screen_window_manager_changed (GdkScreen *screen)
647 {
648   g_signal_emit (screen, signals[WINDOW_MANAGER_CHANGED], 0);
649 }
650
651 /**
652  * _gdk_windowing_substitute_screen_number:
653  * @display_name : The name of a display, in the form used by 
654  *                 gdk_display_open (). If %NULL a default value
655  *                 will be used. On X11, this is derived from the DISPLAY
656  *                 environment variable.
657  * @screen_number : The number of a screen within the display
658  *                  referred to by @display_name.
659  *
660  * Modifies a @display_name to make @screen_number the default
661  * screen when the display is opened.
662  *
663  * Return value: a newly allocated string holding the resulting
664  *   display name. Free with g_free().
665  */
666 gchar * 
667 _gdk_windowing_substitute_screen_number (const gchar *display_name,
668                                          gint         screen_number)
669 {
670   GString *str;
671   gchar   *p;
672
673   if (!display_name)
674     display_name = getenv ("DISPLAY");
675
676   if (!display_name)
677     return NULL;
678
679   str = g_string_new (display_name);
680
681   p = strrchr (str->str, '.');
682   if (p && p >  strchr (str->str, ':'))
683     g_string_truncate (str, p - str->str);
684
685   g_string_append_printf (str, ".%d", screen_number);
686
687   return g_string_free (str, FALSE);
688 }
689
690 /**
691  * gdk_screen_make_display_name:
692  * @screen: a #GdkScreen
693  * 
694  * Determines the name to pass to gdk_display_open() to get
695  * a #GdkDisplay with this screen as the default screen.
696  * 
697  * Return value: a newly allocated string, free with g_free()
698  *
699  * Since: 2.2
700  **/
701 gchar *
702 gdk_screen_make_display_name (GdkScreen *screen)
703 {
704   const gchar *old_display;
705
706   g_return_val_if_fail (GDK_IS_SCREEN (screen), NULL);
707
708   old_display = gdk_display_get_name (gdk_screen_get_display (screen));
709
710   return _gdk_windowing_substitute_screen_number (old_display, 
711                                                   gdk_screen_get_number (screen));
712 }
713
714 #define __GDK_SCREEN_X11_C__
715 #include "gdkaliasdef.c"