zarfy

Artifact [bf68ab4ed1]
Login

Artifact [bf68ab4ed1]

Artifact bf68ab4ed11e3b73fe50507cbdba16de291a1056:


/*
* 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 <http://www.gnu.org/licenses/>.
*
*/

#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; i<scres->noutput; 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; i<scres->noutput; 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; i<scres->ncrtc; 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; i<scres->ncrtc; 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 && y<crtcs[i]->y+crtcs[i]->height )
								dist = abs(x-crtcs[i]->x);
							else
								dist = maxwidth;
							break;
						case 1:
							if ( y>=crtcs[i]->y && y<crtcs[i]->y+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; idx<scres->noutput; 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 ; i<selected.output->nmode; 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; i<selected.output->nclone; 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; i<scres->ncrtc; 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 );
		}
	}
}