/* * Copyright 2008 James S. Allingham * * This file is part of zarfy. * * zarfy 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 3 of the License, or * (at your option) any later version. * * zarfy 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 zarfy. If not, see . * */ #include "zarfy.h" /* expose the crtc map */ gint expose_map () { gdk_draw_drawable(map_da->window, draw_gc, map_pm, 0, 0, 0, 0, map_da->allocation.width, map_da->allocation.height); return FALSE; } /* expose the monitor thumbs */ gint expose_thumbs() { int i; for ( i=0; inoutput; i++) gdk_draw_drawable(mntrs[i]->window, draw_gc, mntr_pms[i], 0, 0, 0, 0, mntrs[i]->allocation.width, mntrs[i]->allocation.height); return FALSE; } gint expose_legend() { int i; for (i=0; inoutput; i++) gdk_draw_drawable(legend_da[i]->window, draw_gc, legend_da_pms[i], 0, 0, 0, 0, legend_da[i]->allocation.width, legend_da[i]->allocation.height); return FALSE; } /* main window About button clicked */ gint about_btn() { int button = 0; while ( button != 2 ) /* 2 = Close button */ button = gtk_dialog_run(about_dialog); gtk_widget_hide((GtkWidget *)about_dialog); return FALSE; } /* About dialog Credits button */ gint do_credits_dialog() { gtk_dialog_run( credits_dialog ); gtk_widget_hide((GtkWidget *)credits_dialog); return FALSE; } /* About dialog License button */ gint do_license_dialog() { gtk_dialog_run( license_dialog ); gtk_widget_hide((GtkWidget *)license_dialog); return FALSE; } /* warning if all crtcs are being turned off */ Bool checkalloff() { int i; for (i=0; incrtc; i++) if (crtcs[i]->mode) break; if ( i < scres->ncrtc ) return FALSE; i = gtk_dialog_run(alloff_warning); gtk_widget_hide((GtkWidget *)alloff_warning); if ( i == 0 ) return TRUE; /* Cancel button */ return FALSE; /* OK button */ } /* don't let a crtc be moved outside the max sreen dimensions */ void check_bounds(XRRCrtcInfo *ci) { ci->x = max(ci->x, 0); if ( (ci->x + ci->width) > maxwidth ) ci->x = maxwidth - ci->width; ci->y = max(ci->y, 0); if ( (ci->y + ci->height) > maxheight ) ci->y = maxheight - ci->height; } /* crtc map area clicked */ gint map_click() { int x, y; double scale = 1/map_scale; if (scale == 0) scale= (double)maxheight / (double)(map_da->allocation.height); if ( selected.crtc == NULL ) return FALSE; /* once seen, get rid of tooltip so as not to annoy */ // gtk_widget_set_has_tooltip ( map_da, FALSE ); gdk_window_get_pointer(map_da->window, &x, &y, NULL); if ( (double)x * scale > maxwidth || (double)y *scale > maxheight ) return FALSE; if ( !gtk_toggle_button_get_active ((GtkToggleButton *)snapto)) { /* snap-to button off: free placement */ selected.crtc->x = (double)x * scale; selected.crtc->y = (double)y * scale; check_bounds(selected.crtc); } else { /* find target for snap-to */ int i, j, dmin, side=0; int n = -1; x = (double)x * scale; y = (double)y * scale; dmin = min(x, y); /* for all active crtcs, find side closest to the click */ for (i=0; incrtc; i++) { if ( crtcs[i]!=selected.crtc && crtcs[i]->mode && crtcs[i]->noutput ) { for (j=0; j<4; j++ ) { int dist=0; switch (j) { case 0: if ( y>=crtcs[i]->y && yy+crtcs[i]->height ) dist = abs(x-crtcs[i]->x); else dist = maxwidth; break; case 1: if ( y>=crtcs[i]->y && yy+crtcs[i]->height ) dist = abs(x-(crtcs[i]->x+crtcs[i]->width)); else dist = maxwidth; break; case 2: if ( x>= crtcs[i]->x && x< crtcs[i]->x+crtcs[i]->width ) dist = abs(y-crtcs[i]->y); else dist = maxheight; break; case 3: if ( x>= crtcs[i]->x && x< crtcs[i]->x+crtcs[i]->width ) dist = abs(y-(crtcs[i]->y+crtcs[i]->height)); else dist = maxheight; } if ( (dmin = min(dmin, dist ) ) == dist ) { n = i; side = j; } } } } if ( n == -1 ) /* none found, snap to origin */ selected.crtc->x = selected.crtc->y = 0; else { int nx=0, ny=0; Bool inside = ( x>=crtcs[n]->x && x<=crtcs[n]->x+crtcs[n]->width && y>=crtcs[n]->y && y<=crtcs[n]->y+crtcs[n]->height ); if ( inside ) { /* same-as */ nx = crtcs[n]->x; ny = crtcs[n]->y; } else { switch (side) { case 0: /* right-of */ nx = crtcs[n]->x-selected.crtc->width; ny = crtcs[n]->y; break; case 1: /* left-of */ nx = crtcs[n]->x+crtcs[n]->width; ny = crtcs[n]->y; break; case 2: /* above */ ny = crtcs[n]->y-selected.crtc->height; nx = crtcs[n]->x; break; case 3: /* below */ ny = crtcs[n]->y+crtcs[n]->height; nx = crtcs[n]->x; } } if ( nx >=0 && nx+selected.crtc->width <= maxwidth && ny>=0 && ny+selected.crtc->height <= maxheight ) { selected.crtc->x = nx; selected.crtc->y = ny; } } } set_screen_size(); draw_map(); draw_legend(); return FALSE; } void set_hw_for_rot(XRRCrtcInfo *ci) { XRRModeInfo *mi = mode_info(ci->mode); if ( ((ci->rotation & 0xf) == RR_Rotate_90 ) || ((ci->rotation & 0xf) == RR_Rotate_270) ) { ci->width = mi->height; ci->height = mi->width; } else { ci->height = mi->height; ci->width = mi->width; } check_bounds(ci); } /* one of the rotation/reflection buttons clicked */ gint rotate(GtkToggleButton *button) { char *lbl=(char *) gtk_button_get_label ((GtkButton *)button); if ( gtk_toggle_button_get_active(button) ) { Rotation rot = selected.crtc->rotation & ROTATE_MASK; Rotation ref = selected.crtc->rotation & REFLECT_MASK; if ( !strcmp(lbl,"Normal") ) rot = RR_Rotate_0; else if ( !strcmp(lbl,"Right") ) rot = RR_Rotate_270; else if ( !strcmp(lbl,"180") ) rot = RR_Rotate_180; else if ( !strcmp(lbl,"Left") ) rot = RR_Rotate_90; else if ( !strcmp(lbl,"Reflect X") ) ref |= RR_Reflect_X; else if ( !strcmp(lbl,"Reflect Y") ) ref |= RR_Reflect_Y; selected.crtc->rotation = rot|ref; } else { if ( !strcmp(lbl,"Reflect X") ) { selected.crtc->rotation &= ~RR_Reflect_X; } else if ( !strcmp(lbl,"Reflect Y") ) { selected.crtc->rotation &= ~RR_Reflect_Y; } } set_hw_for_rot(selected.crtc); set_screen_size(); draw_map(); draw_legend(); return FALSE; } /* mode box selection made */ gint mode_change() { if ( !ignore_combo_change ) { int i = gtk_combo_box_get_active( modebox ); selected.crtc->mode = selected.output->modes[i]; set_hw_for_rot(selected.crtc); set_screen_size(); draw_map(); draw_legend(); } return FALSE; } /* clone combobox selection made */ gint clone_change() { Bool offbtn_changed = FALSE; if ( !ignore_combo_change ) { int i = gtk_combo_box_get_active( clonebox ); Bool on = !gtk_toggle_button_get_active((GtkToggleButton *)offbtn); if (i==0) { /* Clone off */ if ( on ) { /* output was turned on */ gtk_toggle_button_set_active((GtkToggleButton *)offbtn, TRUE); offbtn_toggled(); offbtn_changed = TRUE; } } else { /* Clone on */ XRRCrtcInfo *ci; XRROutputInfo *clone = output_by_id(selected.output->clones[i-1]); if ( on ) turn_off_output(selected.output); selected.output->crtc = clone->crtc; ci = get_crtc(clone->crtc); ci->outputs[ci->noutput++] = output_id(selected.output); if ( !on ) { gtk_toggle_button_set_active((GtkToggleButton *)offbtn, FALSE); onflg = TRUE; offbtn_toggled(); offbtn_changed = TRUE; } } } if ( !offbtn_changed) { set_screen_size(); draw_map(); draw_legend(); } return FALSE; } /* off button clicked (or called from clone_changed) */ gint offbtn_toggled(void) { static struct { RROutput oid; int cycle; int x,y; int width, height; RRMode mode; Rotation rot; } cache; Bool changed; if ( gtk_toggle_button_get_active((GtkToggleButton *)offbtn) ) { /* on -> off */ /* save info in case of re-activation */ cache.cycle = cycle; cache.oid = output_id(selected.output); cache.x = selected.crtc->x; cache.y = selected.crtc->y; cache.width = selected.crtc->width; cache.height = selected.crtc->height; cache.mode = selected.crtc->mode; cache.rot = selected.crtc->rotation; if ( (changed = turn_off_output(selected.output)) ) { draw_monitor(selected.idx, MON_SEL_PIXMAP); selected.crtc = NULL; ignore_combo_change = TRUE; gtk_combo_box_set_active (modebox, 0); ignore_combo_change = FALSE; } else /* turn button back to ON */ gtk_toggle_button_set_active((GtkToggleButton *)offbtn, FALSE); } else { /* off -> on */ if ( (changed = onflg) ) onflg = FALSE; if ( changed || (changed = turn_on_output(selected.output)) ) { selected.crtc = get_crtc(selected.output->crtc); if ( cache.cycle == cycle && cache.oid == output_id(selected.output) ) { selected.crtc->x = cache.x; selected.crtc->y = cache.y; selected.crtc->width = cache.width; selected.crtc->height = cache.height; selected.crtc->mode = cache.mode; selected.crtc->rotation = cache.rot; } } else gtk_toggle_button_set_active((GtkToggleButton *)offbtn, TRUE); } if ( changed ) { setup_selected_widgets(); set_screen_size(); draw_map(); draw_legend(); } return FALSE; } gint apply_button (void) { if ( checkalloff() )return FALSE; deselect_output(); apply(); do_scripts(); write_config(); get_xrr_info(); init_display(); reselect_output(); return FALSE; } gint ok_button (void) { if ( checkalloff() ) return FALSE; apply(); do_scripts(); write_config(); gtk_main_quit(); } /* refresh in case a monitor has been connected/disconnected since program start */ gint refresh_button (GtkLabel *label) { deselect_output(); get_xrr_info(); init_display(); reselect_output(); return FALSE; } void deselect_output() { int i; if (selected.monitor) { draw_monitor(selected.idx, MON_PIXMAP); ignore_combo_change = TRUE; for ( i=selected.output->nmode-1; i>=0; i-- ) gtk_combo_box_remove_text ( modebox, i ); for ( i=selected.output->nclone; i>=0; i-- ) gtk_combo_box_remove_text ( clonebox, i ); ignore_combo_change = FALSE; selected.monitor = NULL; } gdk_window_set_cursor (map_da->window, gdkptr); } void reselect_output() { if ( selected.idx != -1 ) select_output( labels[selected.idx] ); } /* monitor thumb icon clicked */ void select_output (GtkLabel *lbl) { char *ltxt, buf[100]; int idx, i; XRROutputInfo *oi; XRRModeInfo *prefmode; for ( idx=0; idxnoutput; idx++ ) if ( lbl == labels[idx] ) break; if (selected.monitor == mntrs[idx]) return; /* already selected */ ltxt = (char *)gtk_label_get_text(lbl); oi=get_output(ltxt); if (oi->connection == RR_Disconnected) return; /* not a candidate for selection */ deselect_output(); /* deselect previous */ selected.idx = idx; selected.monitor = mntrs[idx]; selected.pm = mntr_pms[idx]; selected.output = oi; selected.crtc = get_crtc(selected.output->crtc); /* OK, user has seen the tooltip, get rid of the annoying thing */ // gtk_widget_set_has_tooltip ( mntrs[idx], FALSE ); /* Give a hint how to change position */ if ( gtk_widget_get_has_tooltip ( map_da ) ) { sprintf( buf, "Click where you\nwant to move %s to", selected.output->name ); gtk_widget_set_tooltip_text ( map_da, buf ); } prefmode = preferred_mode(selected.output); ignore_combo_change = TRUE; /* combo box calls generate an unwanted event */ for ( i=0 ; inmode; i++ ) { XRRModeInfo *mode = mode_info(selected.output->modes[i]); sprintf(buf ,"%s%6.1fHz %s" , mode->name, horiz_refresh(mode) , (prefmode->id == mode->id ? "*" :"" ) ); gtk_combo_box_append_text (modebox, buf); if ( selected.crtc && (mode->id == selected.crtc->mode) ) gtk_combo_box_set_active (modebox, i); } sprintf(buf, "Clone off"); gtk_combo_box_append_text (clonebox, buf); gtk_combo_box_set_active (clonebox, 0); if ( selected.output->nclone ) { for ( i=0; inclone; i++ ) { XRROutputInfo *clone = output_by_id(selected.output->clones[i]); if ( clone->crtc ) { XRRCrtcInfo *ci = get_crtc(clone->crtc); if ( lookup(selected.output->modes, selected.output->nmode, ci->mode) != -1 ) { sprintf(buf, "Clone to %s", clone->name ); gtk_combo_box_append_text (clonebox, buf); } } } gtk_combo_box_set_active (clonebox, get_clone(selected.output)); } ignore_combo_change = FALSE; draw_monitor(selected.idx, MON_SEL_PIXMAP); setup_selected_widgets(); draw_map(); draw_legend(); } void setup_selected_widgets() { char *ltxt; int i; for ( i=0; i<6; i++) { ltxt = (char *)gtk_button_get_label( (GtkButton *)rot[i] ); if ( selected.crtc ) { if ( (!strcmp(ltxt, "Normal") && (selected.crtc->rotations & RR_Rotate_0)) || (!strcmp(ltxt, "Left") && (selected.crtc->rotations & RR_Rotate_90)) || (!strcmp(ltxt, "180") && (selected.crtc->rotations & RR_Rotate_180)) || (!strcmp(ltxt, "Right") && (selected.crtc->rotations & RR_Rotate_270)) || (!strcmp(ltxt, "Reflect X") && (selected.crtc->rotations & RR_Reflect_X)) || (!strcmp(ltxt, "Reflect Y") && (selected.crtc->rotations & RR_Reflect_Y)) ) { gtk_widget_set_sensitive ( (GtkWidget *)rot[i], TRUE ); if ( (!strcmp(ltxt, "Normal") && (selected.crtc->rotation & RR_Rotate_0)) || (!strcmp(ltxt, "Left") && (selected.crtc->rotation & RR_Rotate_90)) || (!strcmp(ltxt, "180") && (selected.crtc->rotation & RR_Rotate_180)) || (!strcmp(ltxt, "Right") && (selected.crtc->rotation & RR_Rotate_270)) || (!strcmp(ltxt, "Reflect X") && (selected.crtc->rotation & RR_Reflect_X)) || (!strcmp(ltxt, "Reflect Y") && (selected.crtc->rotation & RR_Reflect_Y)) ) gtk_toggle_button_set_active ( (GtkToggleButton *)rot[i], TRUE); continue; } } gtk_toggle_button_set_active ( (GtkToggleButton *)rot[i], FALSE); gtk_widget_set_sensitive ( (GtkWidget *)rot[i], FALSE ); } if ( selected.crtc ) { gtk_toggle_button_set_active((GtkToggleButton *)offbtn, FALSE); gtk_widget_set_sensitive ( (GtkWidget *)rbtns, TRUE ); gtk_widget_set_sensitive ( map_da, TRUE ); gdk_window_set_cursor (map_da->window, gdkhand); gtk_widget_set_sensitive ( (GtkWidget *)offbtn, TRUE ); /* to keep things simple, no mode changes allowed while cloned */ if ( selected.crtc->noutput >1 ) gtk_widget_set_sensitive((GtkWidget *)modebox, FALSE); else gtk_widget_set_sensitive((GtkWidget *)modebox, TRUE); } else { gtk_toggle_button_set_active((GtkToggleButton *)offbtn, TRUE); gtk_widget_set_sensitive ( (GtkWidget *)rbtns, FALSE ); gtk_widget_set_sensitive ( map_da, FALSE ); gdk_window_set_cursor (map_da->window, gdkptr ); gtk_widget_set_sensitive((GtkWidget *)modebox, FALSE); if ( get_crtc_for_output(selected.output) == None ) gtk_widget_set_sensitive ( (GtkWidget *)offbtn, FALSE ); else gtk_widget_set_sensitive ( (GtkWidget *)offbtn, TRUE ); } if ( selected.output->nclone ) gtk_widget_set_sensitive((GtkWidget *)clonebox, TRUE); else gtk_widget_set_sensitive((GtkWidget *)clonebox, FALSE); } void set_screen_size() { int i; screen_width = minwidth; screen_height = minheight; for ( i=0; incrtc; i++ ) { if ( crtcs[i]->mode ) { screen_width = max(screen_width, crtcs[i]->x + crtcs[i]->width ); screen_height = max(screen_height, crtcs[i]->y + crtcs[i]->height ); } } }