#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#include <X11/Xproto.h>
#ifdef USE_XRANDR
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/Xrender.h>
#endif
#ifdef USE_XF86VM
#include <X11/extensions/xf86vmode.h>
#endif
#include <gdk/gdkx.h>
#include <libxfce4mcs/mcs-manager.h>
#include <libxfcegui4/libxfcegui4.h>
#include <xfce-mcs-manager/manager-plugin.h>
/* this seems to be a sane value */
#define BORDER 6
/* */
#define GAMMA_MIN 0.1
#define GAMMA_MAX 10.0
/* */
#define RCDIR "mcs_settings"
#define OLDRCDIR "settings"
#define CHANNEL "display"
#define RCFILE "display.xml"
/* */
enum
{
NAME_COLUMN,
SIZE_COLUMN,
RATE_COLUMN,
N_COLUMNS
};
/* settings */
static int sizeIndex = 0;
static int rateIndex = 0;
static int redValue = 100;
static int greenValue = 100;
static int blueValue = 100;
static gboolean syncGamma = TRUE;
/* supported extensions */
static gboolean haveXrandr = FALSE;
static gboolean haveXxf86vm = FALSE;
/* */
static GtkWidget *dialog = NULL;
static GtkWidget *rscale = NULL;
static GtkWidget *gscale = NULL;
static GtkWidget *bscale = NULL;
/* static prototypes */
static void run_dialog (McsPlugin *);
static gboolean save_settings (McsPlugin * plugin);
#ifdef USE_XRANDR
/* */
static void
change_size_and_rate (XRRScreenConfiguration * sc, int size, int rate)
{
Rotation current_rotation;
int current_rate;
int current_size;
gdk_error_trap_push ();
current_rate = XRRConfigCurrentRate (sc);
current_size = XRRConfigCurrentConfiguration (sc, ¤t_rotation);
if (gdk_error_trap_pop ())
{
g_warning ("display_plugin: Unable to query current display resolution");
return;
}
if (size == current_size && rate == current_rate)
{
/* nothing to do here */
return;
}
gdk_error_trap_push ();
XRRSetScreenConfigAndRate (GDK_DISPLAY (), sc, GDK_ROOT_WINDOW (), (SizeID) size,
current_rotation, rate, CurrentTime);
XSync (GDK_DISPLAY (), False);
if (gdk_error_trap_pop ())
{
g_warning ("display_plugin: Unable to configure display resolution");
return;
}
}
#endif
#ifdef USE_XF86VM
/*
*/
static void
change_gamma (double red, double green, double blue)
{
XF86VidModeGamma gamma;
gamma.red = red / 100.0;
gamma.green = green / 100.0;
gamma.blue = blue / 100.0;
gdk_error_trap_push ();
XF86VidModeSetGamma (GDK_DISPLAY (), DefaultScreen (GDK_DISPLAY ()), &gamma);
if (gdk_error_trap_pop ())
{
g_warning ("display_plugin: Unable to configure display gamma correction");
return;
}
}
#endif
/*
*/
McsPluginInitResult
mcs_plugin_init (McsPlugin * plugin)
{
#ifdef USE_XRANDR
XRRScreenConfiguration *sc;
#endif
McsSetting *setting;
gchar *rcfile, *path;
#if defined(USE_XF86VM) || defined(USE_XRANDR)
int major;
int minor;
#endif
xfce_textdomain (GETTEXT_PACKAGE, LOCALEDIR, "UTF-8");
/* read settings channel from file */
path = g_build_filename ("xfce4", RCDIR, RCFILE, NULL);
rcfile = xfce_resource_lookup (XFCE_RESOURCE_CONFIG, path);
if (!rcfile)
{
rcfile = xfce_get_userfile(OLDRCDIR, RCFILE, NULL);
}
if (g_file_test (rcfile, G_FILE_TEST_EXISTS))
{
mcs_manager_add_channel_from_file(plugin->manager, CHANNEL,
rcfile);
}
else
{
mcs_manager_add_channel(plugin->manager, CHANNEL);
}
g_free(path);
g_free(rcfile);
if ((setting = mcs_manager_setting_lookup (plugin->manager, "XDisplay/size", CHANNEL)) != NULL)
{
sizeIndex = setting->data.v_int;
}
else
{
mcs_manager_set_int (plugin->manager, "XDisplay/size", CHANNEL, sizeIndex);
}
if ((setting = mcs_manager_setting_lookup (plugin->manager, "XDisplay/rate", CHANNEL)) != NULL)
{
rateIndex = setting->data.v_int;
}
else
{
mcs_manager_set_int (plugin->manager, "XDisplay/rate", CHANNEL, rateIndex);
}
if ((setting = mcs_manager_setting_lookup (plugin->manager, "XDisplay/rgamma", CHANNEL)) != NULL)
{
redValue = setting->data.v_int;
}
else
{
mcs_manager_set_int (plugin->manager, "XDisplay/rgamma", CHANNEL, redValue);
}
if ((setting = mcs_manager_setting_lookup (plugin->manager, "XDisplay/ggamma", CHANNEL)) != NULL)
{
greenValue = setting->data.v_int;
}
else
{
mcs_manager_set_int (plugin->manager, "XDisplay/ggamma", CHANNEL, greenValue);
}
if ((setting = mcs_manager_setting_lookup (plugin->manager, "XDisplay/bgamma", CHANNEL)) != NULL)
{
blueValue = setting->data.v_int;
}
else
{
mcs_manager_set_int (plugin->manager, "XDisplay/bgamma", CHANNEL, blueValue);
}
if ((setting = mcs_manager_setting_lookup (plugin->manager, "XDisplay/syncGamma", CHANNEL)) != NULL)
{
syncGamma = setting->data.v_int;
}
else
{
mcs_manager_set_int (plugin->manager, "XDisplay/syncGamma", CHANNEL, syncGamma);
}
#ifdef USE_XRANDR
if (XRRQueryVersion (GDK_DISPLAY (), &major, &minor))
{
/* restore resolution and rate */
if ((sc = XRRGetScreenInfo (GDK_DISPLAY (), GDK_ROOT_WINDOW ())))
{
change_size_and_rate (sc, sizeIndex, rateIndex);
XRRFreeScreenConfigInfo (sc);
}
/* remember that we can use the Xrandr extension */
haveXrandr = TRUE;
}
#endif
#ifdef USE_XF86VM
if (XF86VidModeQueryVersion (GDK_DISPLAY (), &major, &minor))
{
/* sync the gamma settings */
if (syncGamma)
{
redValue = greenValue = blueValue = (redValue + greenValue + blueValue) / 3;
}
/* restore gamma settings */
change_gamma (redValue, greenValue, blueValue);
/* remember that we can use the XF86VidMode extension */
haveXxf86vm = TRUE;
}
#endif
plugin->plugin_name = g_strdup ("display");
plugin->caption = g_strdup (_("Display"));
plugin->run_dialog = run_dialog;
plugin->icon = xfce_themed_icon_load ("xfce4-display", 48);
save_settings (plugin);
return (MCS_PLUGIN_INIT_OK);
}
/*
*/
static gboolean
save_settings (McsPlugin * plugin)
{
gboolean result;
gchar *rcfile, *path;
path = g_build_filename ("xfce4", RCDIR, RCFILE, NULL);
rcfile = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, path, TRUE);
result = mcs_manager_save_channel_to_file(plugin->manager, CHANNEL,
rcfile);
g_free(path);
g_free(rcfile);
return (result);
}
#ifdef USE_XRANDR
/*
*/
static void
changedCB (GtkTreeSelection * selection, McsPlugin * plugin)
{
XRRScreenConfiguration *sc;
GtkTreeModel *store;
GtkTreeIter iter;
if (!gtk_tree_selection_get_selected (selection, &store, &iter))
return;
gtk_tree_model_get (store, &iter, RATE_COLUMN, &rateIndex, SIZE_COLUMN, &sizeIndex, -1);
/* apply settings */
sc = XRRGetScreenInfo (GDK_DISPLAY (), GDK_ROOT_WINDOW ());
change_size_and_rate (sc, sizeIndex, rateIndex);
XRRFreeScreenConfigInfo (sc);
/* and remember the settings */
mcs_manager_set_int (plugin->manager, "XDisplay/rate", CHANNEL, rateIndex);
mcs_manager_set_int (plugin->manager, "XDisplay/size", CHANNEL, sizeIndex);
mcs_manager_notify (plugin->manager, CHANNEL);
save_settings (plugin);
}
#endif
#ifdef USE_XF86VM
/*
*/
static void
redChangedCB (GtkRange * range, McsPlugin * plugin)
{
double value;
value = gtk_range_get_value (range);
redValue = (int) (value * 100);
/* sync the other slides */
if (syncGamma)
{
if (greenValue * 100.0 != value)
gtk_range_set_value (GTK_RANGE (gscale), value);
if (blueValue * 100.0 != value)
gtk_range_set_value (GTK_RANGE (bscale), value);
}
/* apply settings */
change_gamma (redValue, greenValue, blueValue);
/* and remember the settings */
mcs_manager_set_int (plugin->manager, "XDisplay/rgamma", CHANNEL, redValue);
mcs_manager_notify (plugin->manager, CHANNEL);
save_settings (plugin);
}
/*
*/
static void
greenChangedCB (GtkRange * range, McsPlugin * plugin)
{
double value;
value = gtk_range_get_value (range);
greenValue = (int) (value * 100);
/* sync the other slides */
if (syncGamma)
{
if (redValue * 100.0 != value)
gtk_range_set_value (GTK_RANGE (rscale), value);
if (blueValue * 100.0 != value)
gtk_range_set_value (GTK_RANGE (bscale), value);
}
/* apply settings */
change_gamma (redValue, greenValue, blueValue);
/* and remember the settings */
mcs_manager_set_int (plugin->manager, "XDisplay/ggamma", CHANNEL, greenValue);
mcs_manager_notify (plugin->manager, CHANNEL);
save_settings (plugin);
}
/*
*/
static void
blueChangedCB (GtkRange * range, McsPlugin * plugin)
{
double value;
value = gtk_range_get_value (range);
blueValue = (int) (value * 100);
/* sync the other slides */
if (syncGamma)
{
if (redValue * 100.0 != value)
gtk_range_set_value (GTK_RANGE (rscale), value);
if (greenValue * 100.0 != value)
gtk_range_set_value (GTK_RANGE (gscale), value);
}
/* apply settings */
change_gamma (redValue, greenValue, blueValue);
/* and remember the settings */
mcs_manager_set_int (plugin->manager, "XDisplay/bgamma", CHANNEL, blueValue);
mcs_manager_notify (plugin->manager, CHANNEL);
save_settings (plugin);
}
/*
*/
static void
syncGammaChangedCB (GtkToggleButton * button, McsPlugin * plugin)
{
gint value;
syncGamma = gtk_toggle_button_get_active (button);
if (syncGamma)
{
value = (redValue + greenValue + blueValue) / 3;
gtk_range_set_value (GTK_RANGE (rscale), value / 100.0);
gtk_range_set_value (GTK_RANGE (gscale), value / 100.0);
gtk_range_set_value (GTK_RANGE (bscale), value / 100.0);
}
mcs_manager_set_int (plugin->manager, "XDisplay/syncGamma", CHANNEL, syncGamma);
mcs_manager_notify (plugin->manager, CHANNEL);
save_settings (plugin);
}
#endif /* !USE_XF86VM */
/*
*/
static void
responseCB (GtkWidget * widget, gint response, McsPlugin * plugin)
{
switch (response)
{
case GTK_RESPONSE_CANCEL:
/* revert settings */
gtk_range_set_value (GTK_RANGE (rscale), 1.0);
gtk_range_set_value (GTK_RANGE (gscale), 1.0);
gtk_range_set_value (GTK_RANGE (bscale), 1.0);
break;
default:
gtk_widget_destroy (dialog);
dialog = NULL;
}
}
/*
*/
static void
run_dialog (McsPlugin * plugin)
{
GtkTreeSelection *selection;
GtkListStore *store;
GtkWidget *revert;
GtkWidget *close;
GtkWidget *header;
GtkWidget *hbox;
GtkWidget *align;
GtkWidget *frame;
GtkWidget *vbox;
GtkWidget *treeview;
GtkWidget *scrollwin;
GtkWidget *table;
GtkWidget *rlabel;
GtkWidget *glabel;
GtkWidget *blabel;
GtkWidget *checkbox;
if (dialog != NULL)
{
gtk_window_present (GTK_WINDOW (dialog));
return;
}
xfce_textdomain (GETTEXT_PACKAGE, LOCALEDIR, "UTF-8");
dialog = gtk_dialog_new_with_buttons (_("Display Preferences"), NULL, GTK_DIALOG_NO_SEPARATOR, NULL);
/* action widgets */
gtk_button_box_set_layout (GTK_BUTTON_BOX (GTK_DIALOG (dialog)->action_area), GTK_BUTTONBOX_EDGE);
revert = gtk_button_new_from_stock (GTK_STOCK_REVERT_TO_SAVED);
gtk_dialog_add_action_widget (GTK_DIALOG (dialog), revert, GTK_RESPONSE_CANCEL);
close = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
gtk_dialog_add_action_widget (GTK_DIALOG (dialog), close, GTK_RESPONSE_CLOSE);
GTK_WIDGET_SET_FLAGS (close, GTK_CAN_DEFAULT);
gtk_widget_grab_default (close);
gtk_widget_grab_focus (close);
gtk_window_set_icon (GTK_WINDOW (dialog), plugin->icon);
gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER);
gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
g_signal_connect_swapped (dialog, "response", G_CALLBACK (responseCB), plugin);
header = xfce_create_header (plugin->icon, _("Display Preferences"));
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), header, FALSE, TRUE, 0);
/* */
hbox = gtk_hbox_new (FALSE, BORDER);
gtk_container_set_border_width (GTK_CONTAINER (hbox), BORDER);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox, TRUE, TRUE, 0);
/* */
align = gtk_alignment_new (0, 0, 0, 0);
gtk_widget_set_size_request (align, BORDER, BORDER);
gtk_box_pack_start (GTK_BOX (hbox), align, FALSE, TRUE, 0);
/* resolution settings */
frame = xfce_framebox_new (_("Resolution"), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
vbox = gtk_vbox_new (FALSE, BORDER);
gtk_container_set_border_width (GTK_CONTAINER (vbox), BORDER);
xfce_framebox_add (XFCE_FRAMEBOX (frame), vbox);
/* create the surrounding scrolled window */
scrollwin = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollwin), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollwin), GTK_SHADOW_IN);
gtk_box_pack_start (GTK_BOX (vbox), scrollwin, TRUE, TRUE, 0);
/* create store */
store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT);
/* create tree view */
treeview = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeview), FALSE);
gtk_widget_set_size_request (treeview, -1, 200);
gtk_container_add (GTK_CONTAINER (scrollwin), treeview);
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
/* create treeview columns */
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview),
gtk_tree_view_column_new_with_attributes ("", gtk_cell_renderer_text_new (), "text", NAME_COLUMN, NULL));
if (haveXrandr)
{
#ifdef USE_XRANDR
XRRScreenConfiguration *sc;
Rotation current_rotation;
XRRScreenSize *sizes;
int nsizes, i, j;
int current_rate;
int current_size;
sc = XRRGetScreenInfo (GDK_DISPLAY (), GDK_ROOT_WINDOW ());
/* XXX */
g_assert (sc != NULL);
sizes = XRRConfigSizes (sc, &nsizes);
current_rate = XRRConfigCurrentRate (sc);
current_size = XRRConfigCurrentConfiguration (sc, ¤t_rotation);
/* fill store */
for (i = 0; i < nsizes; i++)
{
GtkTreeIter iter;
gchar *buffer;
short *rates;
int nrates;
/* query rates for this size */
rates = XRRConfigRates (sc, i, &nrates);
for (j = 0; j < nrates; j++)
{
buffer = g_strdup_printf (_("%dx%d@%d"), sizes[i].width, sizes[i].height, (int) rates[j]);
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter, NAME_COLUMN, buffer, SIZE_COLUMN, (int) i, RATE_COLUMN, (int) rates[j], -1);
if (i == current_size && rates[j] == current_rate)
{
GtkTreePath *path;
path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
gtk_tree_view_set_cursor (GTK_TREE_VIEW (treeview), path, 0, FALSE);
gtk_tree_path_free (path);
}
g_free (buffer);
}
}
/* no longer needed */
XRRFreeScreenConfigInfo (sc);
/* connect the changed callback */
g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (changedCB), plugin);
#endif /* !USE_XRANDR */
}
else
{
/*
* either no XRandr support compiled in, or no Xrandr
* available for the display we are running on
*/
gtk_widget_set_sensitive (treeview, FALSE);
}
/* treeview now keeps a reference on the backstore */
g_object_unref (G_OBJECT (store));
/* gamma settings */
frame = xfce_framebox_new (_("Gamma correction"), TRUE);
gtk_box_pack_start (GTK_BOX (hbox), frame, TRUE, TRUE, 0);
vbox = gtk_vbox_new (FALSE, BORDER);
gtk_container_set_border_width (GTK_CONTAINER (vbox), BORDER);
xfce_framebox_add (XFCE_FRAMEBOX (frame), vbox);
/* */
table = gtk_table_new (3, 3, FALSE);
gtk_box_pack_start (GTK_BOX (vbox), table, TRUE, TRUE, 0);
/* Red */
rlabel = gtk_label_new (_("Red"));
gtk_table_attach (GTK_TABLE (table), rlabel, 0, 1, 0, 1, GTK_FILL, GTK_FILL, BORDER, BORDER);
rscale = gtk_vscale_new_with_range (GAMMA_MIN, GAMMA_MAX, 0.01);
gtk_range_set_inverted (GTK_RANGE (rscale), TRUE);
gtk_range_set_value (GTK_RANGE (rscale), redValue / 100.0);
gtk_scale_set_digits (GTK_SCALE (rscale), 2);
gtk_scale_set_draw_value (GTK_SCALE (rscale), TRUE);
gtk_scale_set_value_pos (GTK_SCALE (rscale), GTK_POS_BOTTOM);
gtk_table_attach (GTK_TABLE (table), rscale, 0, 1, 1, 2, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
/* Green */
glabel = gtk_label_new (_("Green"));
gtk_table_attach (GTK_TABLE (table), glabel, 1, 2, 0, 1, GTK_FILL, GTK_FILL, BORDER, BORDER);
gscale = gtk_vscale_new_with_range (GAMMA_MIN, GAMMA_MAX, 0.01);
gtk_range_set_value (GTK_RANGE (gscale), greenValue / 100.0);
gtk_range_set_inverted (GTK_RANGE (gscale), TRUE);
gtk_scale_set_digits (GTK_SCALE (gscale), 2);
gtk_scale_set_draw_value (GTK_SCALE (gscale), TRUE);
gtk_scale_set_value_pos (GTK_SCALE (gscale), GTK_POS_BOTTOM);
gtk_table_attach (GTK_TABLE (table), gscale, 1, 2, 1, 2, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
/* Blue */
blabel = gtk_label_new (_("Blue"));
gtk_table_attach (GTK_TABLE (table), blabel, 2, 3, 0, 1, GTK_FILL, GTK_FILL, BORDER, BORDER);
bscale = gtk_vscale_new_with_range (GAMMA_MIN, GAMMA_MAX, 0.01);
gtk_range_set_value (GTK_RANGE (bscale), blueValue / 100.0);
gtk_range_set_inverted (GTK_RANGE (bscale), TRUE);
gtk_scale_set_digits (GTK_SCALE (bscale), 2);
gtk_scale_set_draw_value (GTK_SCALE (bscale), TRUE);
gtk_scale_set_value_pos (GTK_SCALE (bscale), GTK_POS_BOTTOM);
gtk_table_attach (GTK_TABLE (table), bscale, 2, 3, 1, 2, GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
/* */
checkbox = gtk_check_button_new_with_label (_("Sync sliders"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox), syncGamma);
gtk_table_attach (GTK_TABLE (table), checkbox, 0, 3, 2, 3, GTK_FILL, GTK_FILL, 0, BORDER);
#ifdef USE_XF86VM
if (haveXxf86vm)
{
g_signal_connect (G_OBJECT (rscale), "value-changed", G_CALLBACK (redChangedCB), plugin);
g_signal_connect (G_OBJECT (gscale), "value-changed", G_CALLBACK (greenChangedCB), plugin);
g_signal_connect (G_OBJECT (bscale), "value-changed", G_CALLBACK (blueChangedCB), plugin);
g_signal_connect (G_OBJECT (checkbox), "toggled", G_CALLBACK (syncGammaChangedCB), plugin);
}
else
#endif
{
gtk_widget_set_sensitive (rlabel, FALSE);
gtk_widget_set_sensitive (rscale, FALSE);
gtk_widget_set_sensitive (glabel, FALSE);
gtk_widget_set_sensitive (gscale, FALSE);
gtk_widget_set_sensitive (blabel, FALSE);
gtk_widget_set_sensitive (bscale, FALSE);
gtk_widget_set_sensitive (checkbox, FALSE);
}
/* */
xfce_gtk_window_center_on_monitor_with_pointer (GTK_WINDOW (dialog));
gtk_widget_show_all (dialog);
}