/* * lxrandr.c - Easy-to-use XRandR GUI frontend for LXDE project * * Copyright (C) 2008 Hong Jen Yee(PCMan) * Copyright (C) 2011 Julien Lavergne * Copyright (C) 2014 Andriy Grytsenko (LStranger) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include typedef enum { PLACEMENT_DEFAULT, PLACEMENT_RIGHT, PLACEMENT_ABOVE, PLACEMENT_LEFT, PLACEMENT_BELOW } MonitorPlacement; typedef struct _Monitor { char* name; GSList* mode_lines; short active_mode; short active_rate; short pref_mode; short pref_rate; short try_mode; short try_rate; MonitorPlacement placement; MonitorPlacement try_placement; GtkCheckButton* enable; #if GTK_CHECK_VERSION(2, 24, 0) GtkComboBoxText* pos_combo; GtkComboBoxText* res_combo; GtkComboBoxText* rate_combo; #else GtkComboBox* pos_combo; GtkComboBox* res_combo; GtkComboBox* rate_combo; #endif }Monitor; static GSList* monitors = NULL; static Monitor* LVDS = NULL; static GtkWidget* dlg = NULL; /* Disable, not used static void monitor_free( Monitor* m ) { g_free( m->name ); g_slist_free( m->mode_lines ); g_free( m ); } */ static const char* get_human_readable_name( Monitor* m ) { if( m == LVDS ) return _("Laptop LCD Monitor"); else if( g_str_has_prefix( m->name, "VGA" ) || g_str_has_prefix( m->name, "Analog" ) ) return LVDS ? _("External VGA Monitor") : _("VGA Monitor"); else if( g_str_has_prefix( m->name, "DVI" ) || g_str_has_prefix(m->name, "TMDS") || g_str_has_prefix(m->name, "Digital") || g_str_has_prefix(m->name, "LVDS") ) return LVDS ? _("External DVI Monitor") : _("DVI Monitor"); else if( g_str_has_prefix( m->name, "TV" ) || g_str_has_prefix(m->name, "S-Video") ) return _("TV"); else if( strcmp( m->name, "default" ) == 0 ) return _( "Default Monitor"); return m->name; } static gboolean get_xrandr_info() { GRegex* regex; GMatchInfo* match; int status; char* output = NULL; char* ori_locale; ori_locale = g_strdup( setlocale(LC_ALL, "") ); // set locale to "C" temporarily to guarantee English output of xrandr setlocale(LC_ALL, "C"); if( ! g_spawn_command_line_sync( "xrandr", &output, NULL, &status, NULL ) || status ) { g_free( output ); setlocale( LC_ALL, ori_locale ); g_free( ori_locale ); return FALSE; } regex = g_regex_new( "\n([-\\.a-zA-Z]+[-\\.0-9]*) +connected ([^(\n ]*)[^\n]*" "((\n +[0-9]+x[0-9]+[^\n]+)+)", 0, 0, NULL ); if( g_regex_match( regex, output, 0, &match ) ) { do { Monitor* m = g_new0( Monitor, 1 ); char *modes = g_match_info_fetch( match, 3 ); char *coords = g_match_info_fetch(match, 2); char **lines, **line; char *ptr; int imode = 0, x = -1, y = -1; m->active_mode = m->active_rate = -1; m->pref_mode = m->pref_rate = -1; m->name = g_match_info_fetch( match, 1 ); ptr = strchr(coords, '+'); if (ptr != NULL) { ptr++; x = strtol(ptr, &ptr, 10); if (*ptr++ == '+') y = strtol(ptr, &ptr, 10); } /* g_debug("name '%s' coords '%s'=>%d:%d modes%.20s...",m->name,coords,x,y,&modes[1]); */ if (x < 0 || y < 0 || (x == 0 && y == 0)) m->placement = PLACEMENT_DEFAULT; else if (x == 0) m->placement = PLACEMENT_BELOW; else if (y == 0) m->placement = PLACEMENT_RIGHT; // check if this is the built-in LCD of laptop if (! LVDS && (g_str_has_prefix(m->name, "LVDS") || g_str_has_prefix(m->name, "PANEL"))) LVDS = m; lines = g_strsplit( modes, "\n", -1 ); for( line = lines; *line; ++line ) { char* str = strtok( *line, " " ); int irate = 0; GPtrArray* strv; if( ! str ) continue; strv = g_ptr_array_sized_new(8); g_ptr_array_add( strv, g_strdup(str) ); while ((str = strtok( NULL, " "))) { if( *str ) { char *star = NULL, *plus = NULL; str = g_strdup( str ); // sometimes, + goes after a space if( 0 == strcmp( str, "+" ) ) --irate; else g_ptr_array_add( strv, str ); if ((star = strchr( str, '*' ))) { m->active_mode = imode; m->active_rate = irate; } if ((plus = strchr( str, '+' ))) { m->pref_mode = imode; m->pref_rate = irate; } if( star ) *star = '\0'; if( plus ) *plus = '\0'; ++irate; } } g_ptr_array_add( strv, NULL ); m->mode_lines = g_slist_append( m->mode_lines, g_ptr_array_free( strv, FALSE ) ); ++imode; } g_strfreev( lines ); g_free( modes ); g_free(coords); monitors = g_slist_prepend( monitors, m ); }while( g_match_info_next( match, NULL ) ); g_match_info_free( match ); } g_regex_unref( regex ); // restore the original locale setlocale( LC_ALL, ori_locale ); g_free( ori_locale ); // Handle the case actually no monitor is added if (! monitors) { return FALSE; } return TRUE; } static void on_enable_toggled(GtkToggleButton *tb, Monitor* m) { GSList *l; Monitor *fixed = LVDS ? LVDS : monitors->data; int i; gboolean can_position; for (l = monitors, i = 0; l; l = l->next) if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m->enable))) i++; can_position = (i > 1); if (monitors->next != NULL) for (l = monitors; l; l = l->next) { Monitor *m = (Monitor*)l->data; gtk_widget_set_sensitive(GTK_WIDGET(m->pos_combo), can_position && (m != fixed)); if (!can_position || m == fixed) gtk_combo_box_set_active(GTK_COMBO_BOX(m->pos_combo), 0); } } static void on_res_sel_changed( GtkComboBox* cb, Monitor* m ) { char** rate; int sel = gtk_combo_box_get_active( cb ); char** mode_line = g_slist_nth_data( m->mode_lines, sel - 1 ); #if GTK_CHECK_VERSION(2, 24, 0) gtk_list_store_clear( GTK_LIST_STORE(gtk_combo_box_get_model( GTK_COMBO_BOX(m->rate_combo) )) ); gtk_combo_box_text_append_text( m->rate_combo, _("Auto") ); if( sel >= 0 && mode_line && *mode_line ) { for( rate = mode_line + 1; *rate; ++rate ) { gtk_combo_box_text_append_text( m->rate_combo, *rate ); } } gtk_combo_box_set_active( GTK_COMBO_BOX(m->rate_combo), 0 ); #else gtk_list_store_clear( GTK_LIST_STORE(gtk_combo_box_get_model(m->rate_combo )) ); gtk_combo_box_append_text( m->rate_combo, _("Auto") ); if( sel >= 0 && mode_line && *mode_line ) { for( rate = mode_line + 1; *rate; ++rate ) { gtk_combo_box_append_text( m->rate_combo, *rate ); } } gtk_combo_box_set_active( m->rate_combo, 0 ); #endif } /*Disable, not used static void open_url( GtkDialog* dlg, const char* url, gpointer data ) { FIXME } */ static void on_about( GtkButton* btn, gpointer parent ) { GtkWidget * about_dlg; const gchar *authors[] = { "洪任諭 Hong Jen Yee (PCMan) ", NULL }; /* TRANSLATORS: Replace mw string with your names, one name per line. */ gchar *translators = _( "translator-credits" ); // gtk_about_dialog_set_url_hook( open_url, NULL, NULL); about_dlg = gtk_about_dialog_new (); gtk_container_set_border_width ( ( GtkContainer*)about_dlg , 2 ); gtk_about_dialog_set_version ( (GtkAboutDialog*)about_dlg, VERSION ); gtk_about_dialog_set_program_name ( (GtkAboutDialog*)about_dlg, _( "LXRandR" ) ); //gtk_about_dialog_set_logo( (GtkAboutDialog*)about_dlg, gdk_pixbuf_new_from_file( PACKAGE_DATA_DIR"/pixmaps/lxrandr.png", NULL ) ); gtk_about_dialog_set_logo_icon_name( (GtkAboutDialog*)about_dlg, "video-display" ); gtk_about_dialog_set_copyright ( (GtkAboutDialog*)about_dlg, _( "Copyright (C) 2008-2014" ) ); gtk_about_dialog_set_comments ( (GtkAboutDialog*)about_dlg, _( "Monitor configuration tool for LXDE" ) ); gtk_about_dialog_set_license ( (GtkAboutDialog*)about_dlg, "This program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nmw program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with mw program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA." ); gtk_about_dialog_set_website ( (GtkAboutDialog*)about_dlg, "http://lxde.org/" ); gtk_about_dialog_set_authors ( (GtkAboutDialog*)about_dlg, authors ); gtk_about_dialog_set_translator_credits ( (GtkAboutDialog*)about_dlg, translators ); gtk_window_set_transient_for( (GtkWindow*)about_dlg, (GtkWindow*)parent ); gtk_dialog_run( ( GtkDialog*)about_dlg ); gtk_widget_destroy( about_dlg ); } static void prepare_try_values_from_GUI() { GSList *l; for (l = monitors; l; l = l->next) { Monitor *m = (Monitor*)l->data; if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m->enable))) m->try_mode = -1; else #if GTK_CHECK_VERSION(2, 24, 0) m->try_mode = gtk_combo_box_get_active(GTK_COMBO_BOX(m->res_combo)); m->try_rate = gtk_combo_box_get_active(GTK_COMBO_BOX(m->rate_combo)); if (m->pos_combo) m->try_placement = gtk_combo_box_get_active(GTK_COMBO_BOX(m->pos_combo)); #else m->try_mode = gtk_combo_box_get_active(m->res_combo); m->try_rate = gtk_combo_box_get_active(m->rate_combo); if (m->pos_combo) m->try_placement = gtk_combo_box_get_active(m->pos_combo); #endif /* g_debug("mode %d rate %d placement %d",m->try_mode,m->try_rate,m->try_placement); */ } } static GString* get_command_xrandr_info() { GSList* l; GString *cmd = g_string_sized_new( 1024 ); g_string_assign( cmd, "sh -c 'xrandr" ); for( l = monitors; l; l = l->next ) { Monitor* m = (Monitor*)l->data; g_string_append( cmd, " --output " ); g_string_append( cmd, m->name ); // if the monitor is turned on if (m->try_mode >= 0) { if( m->try_mode < 1 ) // auto resolution { g_string_append( cmd, " --auto" ); } else { GtkTreeModel *model; GtkTreePath *path; gchar *text; GtkTreeIter iter; gint text_column; g_string_append( cmd, " --mode " ); model = gtk_combo_box_get_model(GTK_COMBO_BOX(m->res_combo)); text_column = gtk_combo_box_get_entry_text_column(GTK_COMBO_BOX(m->res_combo)); path = gtk_tree_path_new_from_indices(m->try_mode, -1); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, text_column, &text, -1); gtk_tree_path_free(path); g_string_append(cmd, text); g_free(text); if( m->try_rate >= 1 ) // not auto refresh rate { g_string_append( cmd, " --rate " ); model = gtk_combo_box_get_model(GTK_COMBO_BOX(m->rate_combo)); text_column = gtk_combo_box_get_entry_text_column(GTK_COMBO_BOX(m->rate_combo)); path = gtk_tree_path_new_from_indices(m->try_rate, -1); gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, text_column, &text, -1); gtk_tree_path_free(path); g_string_append(cmd, text); g_free(text); } } if (m == LVDS) ; /* it's fixed, no positioning */ else if (LVDS != NULL) { switch (m->try_placement) { case PLACEMENT_RIGHT: g_string_append(cmd, " --right-of "); break; case PLACEMENT_ABOVE: g_string_append(cmd, " --above "); break; case PLACEMENT_LEFT: g_string_append(cmd, " --left-of "); break; case PLACEMENT_BELOW: g_string_append(cmd, " --below "); break; case PLACEMENT_DEFAULT: g_string_append(cmd, " --same-as "); } g_string_append(cmd, LVDS->name); } else if (l != monitors) { Monitor *first = (Monitor*)monitors->data; /* not notebook */ switch (m->try_placement) { case PLACEMENT_RIGHT: g_string_append(cmd, " --right-of "); break; case PLACEMENT_ABOVE: g_string_append(cmd, " --above "); break; case PLACEMENT_LEFT: g_string_append(cmd, " --left-of "); break; case PLACEMENT_BELOW: g_string_append(cmd, " --below "); break; case PLACEMENT_DEFAULT: g_string_append(cmd, " --same-as "); } g_string_append(cmd, first->name); } /* g_string_append( cmd, "" ); */ } else // turn off g_string_append( cmd, " --off" ); } g_string_append_c(cmd, '\''); return cmd; } static void save_configuration() { char* dirname; const char grp[] = "Desktop Entry"; GKeyFile* kf; char* file, *data; gsize len; GString *cmd; prepare_try_values_from_GUI(); cmd = get_command_xrandr_info(); /* create user autostart dir */ dirname = g_build_filename(g_get_user_config_dir(), "autostart", NULL); g_mkdir_with_parents(dirname, 0700); g_free(dirname); kf = g_key_file_new(); g_key_file_set_string( kf, grp, "Type", "Application" ); g_key_file_set_string( kf, grp, "Name", _("LXRandR autostart") ); g_key_file_set_string( kf, grp, "Comment", _("Start xrandr with settings done in LXRandR") ); g_key_file_set_string( kf, grp, "Exec", cmd->str ); data = g_key_file_to_data(kf, &len, NULL); file = g_build_filename( g_get_user_config_dir(), "autostart", "lxrandr-autostart.desktop", NULL ); /* save it to user-specific autostart dir */ g_debug("save to: %s", file); g_file_set_contents(file, data, len, NULL); g_key_file_free (kf); g_free(file); g_free(data); g_string_free(cmd, TRUE); } static gboolean cancel_confirmation(gpointer data) { if (!g_source_is_destroyed(g_main_current_source())) gtk_dialog_response(data, GTK_RESPONSE_CANCEL); return FALSE; } static void set_xrandr_info() { GString *cmd; GSList *l; prepare_try_values_from_GUI(); cmd = get_command_xrandr_info(); /* g_debug("trying: %s", cmd->str); */ if (g_spawn_command_line_sync( cmd->str, NULL, NULL, NULL, NULL )) { /* open a dialog box and wait 15 seconds */ GtkWidget *confirmation; guint timer; int responce; confirmation = gtk_message_dialog_new(GTK_WINDOW(dlg), GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("Is everything OK? Confirm within 15 seconds," " otherwise previous state will be restored.")); gtk_dialog_add_buttons(GTK_DIALOG(confirmation), _("_OK"), GTK_RESPONSE_ACCEPT, _("_Abort"), GTK_RESPONSE_CANCEL, NULL); gtk_dialog_set_default_response(GTK_DIALOG(confirmation), GTK_RESPONSE_CANCEL); timer = gdk_threads_add_timeout(15000, cancel_confirmation, confirmation); responce = gtk_dialog_run(GTK_DIALOG(confirmation)); g_source_remove(timer); gtk_widget_destroy(confirmation); /* if not confirmed then set GUI to fallback values, reset xrandr, then restore all GUI again */ if (responce == GTK_RESPONSE_ACCEPT) { for (l = monitors; l; l = l->next) { Monitor *m = (Monitor*)l->data; m->active_mode = m->try_mode; m->active_rate = m->try_rate; m->placement = m->try_placement; } } else { for (l = monitors; l; l = l->next) { Monitor *m = (Monitor*)l->data; m->try_mode = m->active_mode; m->try_rate = m->active_rate; m->try_placement = m->placement; } g_string_free(cmd, TRUE); cmd = get_command_xrandr_info(); /* g_debug("recovering: %s", cmd->str); */ g_spawn_command_line_sync(cmd->str, NULL, NULL, NULL, NULL); } } g_string_free( cmd, TRUE ); } static void choose_max_resolution( Monitor* m ) { #if GTK_CHECK_VERSION(2, 24, 0) if( gtk_tree_model_iter_n_children( gtk_combo_box_get_model(GTK_COMBO_BOX(m->res_combo)), NULL ) > 1 ) gtk_combo_box_set_active( GTK_COMBO_BOX(m->res_combo), 1 ); if (m->pos_combo) gtk_combo_box_set_active(GTK_COMBO_BOX(m->pos_combo), PLACEMENT_DEFAULT); #else if( gtk_tree_model_iter_n_children( gtk_combo_box_get_model(m->res_combo), NULL ) > 1 ) gtk_combo_box_set_active( m->res_combo, 1 ); if (m->pos_combo) gtk_combo_box_set_active(m->pos_combo, PLACEMENT_DEFAULT); #endif } static void on_quick_option( GtkButton* btn, gpointer data ) { GSList* l; int option = GPOINTER_TO_INT(data); switch( option ) { case 1: // turn on both for( l = monitors; l; l = l->next ) { Monitor* m = (Monitor*)l->data; choose_max_resolution( m ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(m->enable), TRUE ); } break; case 2: // external monitor only for( l = monitors; l; l = l->next ) { Monitor* m = (Monitor*)l->data; choose_max_resolution( m ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(m->enable), m != LVDS ); } break; case 3: // laptop panel - LVDS only for( l = monitors; l; l = l->next ) { Monitor* m = (Monitor*)l->data; choose_max_resolution( m ); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(m->enable), m == LVDS ); } break; case 4: // external right of LVDS for( l = monitors; l; l = l->next ) { Monitor* m = (Monitor*)l->data; choose_max_resolution( m ); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m->enable), TRUE); if (m != LVDS) gtk_combo_box_set_active(GTK_COMBO_BOX(m->pos_combo), PLACEMENT_RIGHT); } break; case 5: // external above of LVDS for( l = monitors; l; l = l->next ) { Monitor* m = (Monitor*)l->data; choose_max_resolution( m ); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m->enable), TRUE); if (m != LVDS) gtk_combo_box_set_active(GTK_COMBO_BOX(m->pos_combo), PLACEMENT_ABOVE); } break; default: return; } // gtk_dialog_response( GTK_DIALOG(dlg), GTK_RESPONSE_OK ); // set_xrandr_info(); } static void on_response( GtkDialog* dialog, int response, gpointer user_data ) { if( response == GTK_RESPONSE_OK ) { GtkWidget* msg; GSList* l; for( l = monitors; l; l = l->next ) { Monitor* m = (Monitor*)l->data; if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(m->enable) ) ) break; } if (l != NULL) set_xrandr_info(); else { msg = gtk_message_dialog_new( GTK_WINDOW(dialog), 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, _("You cannot turn off all monitors. " "Otherwise, you will not be able to " "turn them on again since this tool " "is not accessible without monitor.") ); gtk_dialog_run( GTK_DIALOG(msg) ); gtk_widget_destroy( msg ); } // block the response g_signal_stop_emission_by_name( dialog, "response" ); } else if (response == GTK_RESPONSE_ACCEPT) { GtkWidget* msg; save_configuration(); msg = gtk_message_dialog_new( GTK_WINDOW(dialog), 0, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, _("Configuration Saved") ); gtk_dialog_run( GTK_DIALOG(msg) ); gtk_widget_destroy( msg ); } } int main(int argc, char** argv) { GtkWidget *notebook, *vbox, *frame, *label, *hbox, *check, *btn; GSList* l; Monitor *fixed; int i; gboolean can_position; #ifdef ENABLE_NLS bindtextdomain ( GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR ); bind_textdomain_codeset ( GETTEXT_PACKAGE, "UTF-8" ); textdomain ( GETTEXT_PACKAGE ); #endif gtk_init( &argc, &argv ); if( ! get_xrandr_info() ) { dlg = gtk_message_dialog_new( NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Unable to get monitor information!")); gtk_dialog_run( (GtkDialog*)dlg ); gtk_widget_destroy( dlg ); return 1; } dlg = gtk_dialog_new_with_buttons( _("Display Settings"), NULL, GTK_DIALOG_MODAL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, GTK_STOCK_APPLY, GTK_RESPONSE_OK, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL ); g_signal_connect( dlg, "response", G_CALLBACK(on_response), NULL ); gtk_container_set_border_width( GTK_CONTAINER(dlg), 8 ); gtk_dialog_set_alternative_button_order( GTK_DIALOG(dlg), GTK_RESPONSE_OK, GTK_RESPONSE_CANCEL, -1 ); /* Set icon name for main (dlg) window so it displays in the panel. */ gtk_window_set_icon_name(GTK_WINDOW(dlg), "video-display"); btn = gtk_button_new_from_stock( GTK_STOCK_ABOUT ); #if GTK_CHECK_VERSION(2,14,0) gtk_box_pack_start(GTK_BOX(gtk_dialog_get_action_area( GTK_DIALOG(dlg))), btn, FALSE, TRUE, 0 ); gtk_button_box_set_child_secondary( GTK_BUTTON_BOX(gtk_dialog_get_action_area( GTK_DIALOG(dlg))), btn, TRUE ); #else gtk_box_pack_start( GTK_BOX(GTK_DIALOG(dlg)->action_area), btn, FALSE, TRUE, 0 ); gtk_button_box_set_child_secondary( GTK_BUTTON_BOX(GTK_DIALOG(dlg)->action_area), btn, TRUE ); #endif g_signal_connect( btn, "clicked", G_CALLBACK(on_about), dlg ); notebook = gtk_notebook_new(); #if GTK_CHECK_VERSION(2,14,0) gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dlg))), notebook, TRUE, TRUE, 2 ); #else gtk_box_pack_start( GTK_BOX( GTK_DIALOG(dlg)->vbox ), notebook, TRUE, TRUE, 2 ); #endif // If this is a laptop and there is an external monitor, offer quick options if( LVDS && g_slist_length( monitors ) == 2 ) { vbox = gtk_vbox_new( FALSE, 4 ); gtk_container_set_border_width( GTK_CONTAINER(vbox), 8 ); btn = gtk_radio_button_new_with_label(NULL, _("Show the same screen on both laptop LCD and external monitor")); g_signal_connect( btn, "clicked", G_CALLBACK(on_quick_option), GINT_TO_POINTER(1) ); gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, TRUE , 4); btn = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(btn), _("Turn off laptop LCD and use external monitor only")); g_signal_connect( btn, "clicked", G_CALLBACK(on_quick_option), GINT_TO_POINTER(2) ); gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, TRUE , 4); btn = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(btn), _("Turn off external monitor and use laptop LCD only")); g_signal_connect( btn, "clicked", G_CALLBACK(on_quick_option), GINT_TO_POINTER(3) ); gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, TRUE , 4); btn = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(btn), _("Place external monitor to the right of laptop LCD")); g_signal_connect( btn, "clicked", G_CALLBACK(on_quick_option), GINT_TO_POINTER(4) ); gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, TRUE , 4); btn = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(btn), _("Place external monitor above of laptop LCD")); g_signal_connect( btn, "clicked", G_CALLBACK(on_quick_option), GINT_TO_POINTER(5) ); gtk_box_pack_start( GTK_BOX(vbox), btn, FALSE, TRUE , 4); gtk_notebook_append_page( GTK_NOTEBOOK(notebook), vbox, gtk_label_new( _("Quick Options") ) ); } else { gtk_notebook_set_show_tabs( GTK_NOTEBOOK(notebook), FALSE ); } vbox = gtk_vbox_new( FALSE, 4 ); gtk_container_set_border_width( GTK_CONTAINER(vbox), 8 ); gtk_notebook_append_page( GTK_NOTEBOOK(notebook), vbox, gtk_label_new(_("Advanced")) ); label = gtk_label_new(""); gtk_misc_set_alignment( GTK_MISC(label), 0.0, 0.5 ); gtk_label_set_markup( GTK_LABEL(label), ngettext( "The following monitor is detected:", "The following monitors are detected:", g_slist_length(monitors) ) ); gtk_box_pack_start( GTK_BOX(vbox), label, FALSE, TRUE, 2 ); for (l = monitors, i = 0; l; l = l->next) if (((Monitor*)l->data)->active_mode >= 0) i++; can_position = (i > 1); /* correct placements */ fixed = LVDS ? LVDS : monitors->data; if (fixed->placement != PLACEMENT_DEFAULT) { for (l = monitors, i = 0; l; l = l->next) { Monitor *m = (Monitor*)l->data; if (m->placement != PLACEMENT_DEFAULT) continue; /* FIXME: how to handle corners? */ switch (fixed->placement) { case PLACEMENT_LEFT: m->placement = PLACEMENT_RIGHT; break; case PLACEMENT_RIGHT: m->placement = PLACEMENT_LEFT; break; case PLACEMENT_ABOVE: m->placement = PLACEMENT_BELOW; break; case PLACEMENT_BELOW: m->placement = PLACEMENT_ABOVE; break; case PLACEMENT_DEFAULT: ; } } fixed->placement = PLACEMENT_DEFAULT; } for( l = monitors, i = 0; l; l = l->next, ++i ) { Monitor* m = (Monitor*)l->data; GSList* mode_line; frame = gtk_frame_new( get_human_readable_name(m) ); gtk_box_pack_start( GTK_BOX(vbox), frame, FALSE, TRUE, 2 ); hbox = gtk_hbox_new( FALSE, 4 ); gtk_container_set_border_width( GTK_CONTAINER(hbox), 4 ); gtk_container_add( GTK_CONTAINER(frame), hbox ); check = gtk_check_button_new_with_label( _("Turn On") ); m->enable = GTK_CHECK_BUTTON(check); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), m->active_mode >= 0); // turn off screen is not allowed since there should be at least one monitor available. if( g_slist_length( monitors ) == 1 ) gtk_widget_set_sensitive( GTK_WIDGET(m->enable), FALSE ); else g_signal_connect(m->enable, "toggled", G_CALLBACK(on_enable_toggled), m); gtk_box_pack_start( GTK_BOX(hbox), check, FALSE, TRUE, 6 ); if (monitors->next != NULL) /* g_slist_length(monitors) > 0 */ { label = gtk_label_new(_("Position:")); gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 2); #if GTK_CHECK_VERSION(2, 24, 0) m->pos_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); gtk_combo_box_text_append_text(m->pos_combo, _("Default")); gtk_combo_box_text_append_text(m->pos_combo, _("On right")); gtk_combo_box_text_append_text(m->pos_combo, _("Above")); gtk_combo_box_text_append_text(m->pos_combo, _("On left")); gtk_combo_box_text_append_text(m->pos_combo, _("Below")); gtk_combo_box_set_active(GTK_COMBO_BOX(m->pos_combo), m->placement); #else m->pos_combo = GTK_COMBO_BOX(gtk_combo_box_new_text()); gtk_combo_box_append_text(m->res_combo, _("Default")); gtk_combo_box_append_text(m->res_combo, _("On right")); gtk_combo_box_append_text(m->res_combo, _("Above")); gtk_combo_box_append_text(m->pos_combo, _("On left")); gtk_combo_box_append_text(m->pos_combo, _("Below")); gtk_combo_box_set_active(m->pos_combo, m->placement); #endif if (m == fixed || !can_position || m->active_mode < 0) gtk_widget_set_sensitive(GTK_WIDGET(m->pos_combo), FALSE); gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(m->pos_combo), FALSE, TRUE, 2); } label = gtk_label_new( _("Resolution:") ); gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, TRUE, 2 ); #if GTK_CHECK_VERSION(2, 24, 0) m->res_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); #else m->res_combo = GTK_COMBO_BOX(gtk_combo_box_new_text()); #endif g_signal_connect( m->res_combo, "changed", G_CALLBACK(on_res_sel_changed), m ); gtk_box_pack_start( GTK_BOX(hbox), GTK_WIDGET(m->res_combo), FALSE, TRUE, 2 ); label = gtk_label_new( _("Refresh Rate:") ); gtk_box_pack_start( GTK_BOX(hbox), label, FALSE, TRUE, 2 ); #if GTK_CHECK_VERSION(2, 24, 0) m->rate_combo = GTK_COMBO_BOX_TEXT(gtk_combo_box_text_new()); #else m->rate_combo = GTK_COMBO_BOX(gtk_combo_box_new_text()); #endif gtk_box_pack_start( GTK_BOX(hbox), GTK_WIDGET(m->rate_combo), FALSE, TRUE, 2 ); #if GTK_CHECK_VERSION(2, 24, 0) gtk_combo_box_text_append_text( m->res_combo, _("Auto") ); #else gtk_combo_box_append_text( m->res_combo, _("Auto") ); #endif for( mode_line = m->mode_lines; mode_line; mode_line = mode_line->next ) { char** strv = (char**)mode_line->data; #if GTK_CHECK_VERSION(2, 24, 0) gtk_combo_box_text_append_text( m->res_combo, strv[0] ); #else gtk_combo_box_append_text( m->res_combo, strv[0] ); #endif } m->active_rate++; #if GTK_CHECK_VERSION(2, 24, 0) gtk_combo_box_set_active( GTK_COMBO_BOX(m->res_combo), m->active_mode + 1 ); gtk_combo_box_set_active( GTK_COMBO_BOX(m->rate_combo), m->active_rate ); #else gtk_combo_box_set_active( m->res_combo, m->active_mode + 1 ); gtk_combo_box_set_active( m->rate_combo, m->active_rate ); #endif if (m->active_mode >= 0) m->active_mode++; /* let it stay -1 for inactive button */ } gtk_widget_show_all( dlg ); gtk_dialog_run((GtkDialog*)dlg); gtk_widget_destroy( dlg ); return 0; }