[Muine] [PATCH] C# implementations of MessageConnection and VolumeButton



Hello,

I've been using muine as a jumping off point for learning C# and mono.
My first task was to implement the MessageConnection class and volume
button in C#. I did this a while ago and was reminding about it after
seeing Bastien Nocera's blog post [1] about various programs still
using his "horror" bacon-message-connection. I suppose that this patch
might be of some value to the project since it reduces the size of the
code base and makes it arguably more maintainable.

I see that one other developer also posted a more complete patch
fixing some GC'd delegates. It would be nice to get that patch
committed so that muine will work with Mono 1.1.1.

Cheers,
                       Ross Girshick

[1] planet.gnome.org, sept. 27th.


-- 
"Magic is real, unless declared integer."
Index: libmuine/Makefile.am
===================================================================
RCS file: /cvs/gnome/muine/libmuine/Makefile.am,v
retrieving revision 1.10
diff -r1.10 Makefile.am
51,52d50
< 	volume-button.c			\
< 	volume-button.h			\
55,56d52
< 	bacon-message-connection.c	\
< 	bacon-message-connection.h	\
Index: src/Makefile.am
===================================================================
RCS file: /cvs/gnome/muine/src/Makefile.am,v
retrieving revision 1.36
diff -r1.36 Makefile.am
41,42c41,44
< 	/r:AmazonSearchService.dll		\
< 	/r:System.Web.Services
---
> 	-r:AmazonSearchService.dll		\
> 	-r:System.Web.Services			\
> 	-r:Mono.Posix
> 	
Index: src/MessageConnection.cs
===================================================================
RCS file: /cvs/gnome/muine/src/MessageConnection.cs,v
retrieving revision 1.2
diff -r1.2 MessageConnection.cs
2c2
<  * Copyright (C) 2004 Jorn Baayen <jorn nl linux org>
---
>  * Copyright (C) 2004 Ross Girshick <ross girshick gmail com>
21c21,25
< using System.Runtime.InteropServices;
---
> using System.Net;
> using System.Net.Sockets;
> using Mono.Posix;
> using System.Text;
> using System.IO;
25c29,34
< 	private IntPtr conn;
---
> 	public enum StatusCode { 
> 		OK, 
> 		Retry, 
> 		SocketCreateFailure, 
> 		SocketDeleteFailure 
> 	};
27,28c36,39
< 	[DllImport ("libmuine")]
< 	private static extern IntPtr bacon_message_connection_new (string name);
---
> 	public enum ConnectionType { 
> 		Server, 
> 		Client 
> 	};
30,32c41,47
< 	public MessageConnection ()
< 	{
< 		conn = bacon_message_connection_new ("Muine");
---
> 	private Socket socket;
> 	
> 	private string socket_filename;
> 	public string SocketFilename {
> 		get {
> 			return socket_filename;
> 		}
35,36c50,55
< 	[DllImport ("libmuine")]
< 	private static extern bool bacon_message_connection_get_is_server (IntPtr conn);
---
> 	private ConnectionType role;
> 	public ConnectionType Role {
> 		get {
> 			return role;
> 		}
> 	}
38c57,58
< 	public bool IsServer {
---
> 	private StatusCode status;
> 	public StatusCode Status {
40c60
< 			return bacon_message_connection_get_is_server (conn);
---
> 			return status;
44,45c64
< 	public delegate void MessageReceivedHandler (string message,
< 						     IntPtr user_data);
---
> 	public delegate void MessageReceivedDelegate (string Message);
47,50c66,71
< 	[DllImport ("libmuine")]
< 	private static extern void bacon_message_connection_set_callback (IntPtr conn,
< 									  MessageReceivedHandler callback,
< 									  IntPtr user_data);
---
> 	private MessageReceivedDelegate message_received_handler;
> 	public MessageReceivedDelegate MessageReceivedHandler {
> 		set {
> 			message_received_handler = value;
> 		}
> 	}
52c73,74
< 	public void SetCallback (MessageReceivedHandler handler)
---
> 
> 	public MessageConnection ()
54c76,121
< 		bacon_message_connection_set_callback (conn, handler, IntPtr.Zero);
---
> 		string socket_path;
> 		
> 		try {
> 			socket_path = System.IO.Path.GetTempPath ();
> 		} catch {
> 			socket_path = "/tmp";
> 		}
> 
> 		status = StatusCode.OK;
> 		
> 		socket_filename = System.IO.Path.Combine (socket_path, "muine-" + Environment.GetEnvironmentVariable ("USER") + ".socket");
> 				
> 		socket = new Socket (AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
> 		EndPoint socket_end_point = new UnixEndPoint (socket_filename);
> 
> 		if (File.Exists (socket_filename)) {
> 			role = ConnectionType.Client;
> 			try {
> 				socket.Connect (socket_end_point);
> 			} catch {
> 				Console.WriteLine ("Socket already exists, removing.");
> 				try {
> 					File.Delete (socket_filename);
> 					status = StatusCode.Retry;
> 				} catch	{
> 					status = StatusCode.SocketDeleteFailure;
> 				}
> 			}
> 		} else {
> 			role = ConnectionType.Server;
> 
> 			try {
> 				socket.Bind (socket_end_point);
> 				socket.Listen (5);
> 				socket.BeginAccept (new AsyncCallback (ListenCallback), socket);
> 			}
> 			catch
> 			{
> 				status = StatusCode.SocketCreateFailure;
> 			}
> 		}
> 	}
> 	
> 	~ MessageConnection ()
> 	{
> 		Close ();
57,58c124,127
< 	[DllImport ("libmuine")]
< 	private static extern void bacon_message_connection_send (IntPtr conn, string command);
---
> 	public int Send (string Message)
> 	{
> 		return socket.Send (Encoding.ASCII.GetBytes (Message));
> 	}
60c129
< 	public void Send (string command)
---
> 	public void Close ()
62c131,134
< 		bacon_message_connection_send (conn, command);
---
> 		if (role == ConnectionType.Server) {
> 			Console.WriteLine ("Removing socket file");
> 			File.Delete (socket_filename);
> 		}
65,66c137,150
< 	[DllImport ("libmuine")]
< 	private static extern void bacon_message_connection_free (IntPtr conn);
---
> 	private void ListenCallback (IAsyncResult state)
> 	{
> 		Socket Client = ((Socket)state.AsyncState).EndAccept (state);
> 		((Socket)state.AsyncState).BeginAccept (new AsyncCallback (ListenCallback), state.AsyncState);
> 		byte[] buf = new byte[1024];
> 		Client.Receive (buf);
> 
> 		string raw_message = Encoding.ASCII.GetString (buf);
> 		string message = "";
> 		foreach (char c in raw_message) {
> 			if (c == 0x0000)
> 				break;
> 			message += c;
> 		}
68c152,155
< 	public void Close ()
---
> 		GLib.Idle.Add (new GLib.IdleHandler (new IdleWork (message_received_handler, message).Run));
> 	}
> 
> 	internal class IdleWork
70c157,170
< 		bacon_message_connection_free (conn);
---
> 		private string message;
> 		private MessageReceivedDelegate callback;
> 
> 		public IdleWork (MessageReceivedDelegate cb, string msg)
> 		{
> 			message = msg;
> 			callback = cb;
> 		}
> 
> 		public bool Run ()
> 		{
> 			callback (message);
> 			return false;
> 		}
Index: src/Muine.cs
===================================================================
RCS file: /cvs/gnome/muine/src/Muine.cs,v
retrieving revision 1.31
diff -r1.31 Muine.cs
60c60,71
< 		if (!conn.IsServer) {
---
> 		while (conn.Status == MessageConnection.StatusCode.Retry) {
> 			conn = new MessageConnection ();
> 		}
> 
> 		if (conn.Status != MessageConnection.StatusCode.OK) {
> 			Console.WriteLine ("Error creating MessageConnection: " + conn.Status);
> 			Environment.Exit (1);
> 		}
> 		
> 		/* An instance already exists. Handle command line args and exit. */
> 		if (conn.Role == MessageConnection.ConnectionType.Client)
> 		{
103c114
< 		conn.SetCallback (new MessageConnection.MessageReceivedHandler (HandleMessageReceived));
---
> 		conn.MessageReceivedHandler = new MessageConnection.MessageReceivedDelegate (HandleMessageReceived);
105a117
> 
170,171c182
< 	private void HandleMessageReceived (string message,
< 					    IntPtr user_data)
---
> 	private void HandleMessageReceived (string message)
Index: src/VolumeButton.cs
===================================================================
RCS file: /cvs/gnome/muine/src/VolumeButton.cs,v
retrieving revision 1.4
diff -r1.4 VolumeButton.cs
2c2
<  * Copyright (C) 2004 Jorn Baayen <jorn nl linux org>
---
>  * Copyright (C) 2004 Ross Girshick <ross girshick gmail com>
20,24d19
< using System;
< using System.Collections;
< using System.Runtime.InteropServices;
< 
< using Gtk;
25a21,22
> using Gtk;
> using System;
27c24
< public class VolumeButton : Button
---
> class VolumeButton : ToggleButton
29,30c26,29
< 	[DllImport ("libmuine")]
< 	private static extern IntPtr volume_button_new ();
---
> 	private Image icon;
> 	private Window popup;
> 	private int volume;
> 	private int revert_volume;
32,35c31,32
< 	[DllImport ("libgobject-2.0-0.dll")]
< 	private static extern uint g_signal_connect_data (IntPtr obj, string name,
< 							  SignalDelegate cb, IntPtr data,
< 							  IntPtr p, int flags);
---
> 	/* GDK_CURRENT_TIME doesn't seem to have an equiv in gtk-sharp yet. */
> 	const uint CURRENT_TIME = 0;
37,39c34,60
< 	public VolumeButton () : base (IntPtr.Zero)
< 	{
< 		Raw = volume_button_new ();
---
> 	public int Volume {
> 		set {
> 			string id = "muine-volume-";
> 			
> 			volume = value;
> 
> 			if (volume <= 0)
> 				id += "zero";
> 			else if (volume <= 100 / 3)
> 				id += "min";
> 			else if (volume <= 200 / 3)
> 				id += "medium";
> 			else
> 				id += "max";
> 
> 			icon.SetFromStock (id, IconSize.LargeToolbar);
> 
> 			VolumeChanged (Volume);
> 		}
> 
> 		get {
> 			return volume;
> 		}
> 	}
> 
> 	public delegate void VolumeChangedHandler (int vol);
> 	public event VolumeChangedHandler VolumeChanged;
41,42c62,73
< 		g_signal_connect_data (Raw, "volume_changed", new SignalDelegate (VolumeChangedCallback),
< 				       IntPtr.Zero, IntPtr.Zero, 0);
---
> 	public VolumeButton () : base ()
> 	{
> 		icon = new Image ();
> 		icon.Show ();
> 		Add (icon);
> 
> 		popup = null;
> 
> 		ScrollEvent += new ScrollEventHandler (ScrollHandler);
> 		Toggled += new EventHandler (ToggleHandler);
> 		
> 		Flags |= (int)WidgetFlags.NoWindow;
50,55c81,165
< 	[DllImport ("libmuine")]
< 	private static extern void volume_button_set_volume (IntPtr btn, int vol);
< 	
< 	public int Volume {
< 		set {
< 			volume_button_set_volume (Raw, value);
---
> 	private void ShowScale ()
> 	{
> 		VScale scale;
> 		VBox box;
> 		Adjustment adj;
> 		Frame frame;
> 		Label label;
> 		Image vol_icon_top;
> 		Image vol_icon_bottom;
> 		Requisition req;
> 		int x, y;
> 		
> 		/* Check point the volume so that we can restore it if the user rejects the slider changes. */
> 		revert_volume = Volume;
> 
> 		popup = new Window (WindowType.Popup);
> 		popup.Screen = this.Screen;
> 
> 		frame = new Frame ();
> 		frame.Shadow = ShadowType.Out;
> 		frame.Show ();
> 
> 		popup.Add (frame);
> 
> 		box = new VBox (false, 0);
> 		box.Show();
> 
> 		frame.Add (box);
> 
> 		adj = new Adjustment (volume, 0, 100, 5, 10, 0);		
> 
> 		scale = new VScale (adj);
> 		scale.ValueChanged += new EventHandler (ScaleValueChanged);
> 		scale.KeyPressEvent += new KeyPressEventHandler (ScaleKeyPressed);
> 		popup.ButtonPressEvent += new ButtonPressEventHandler (PopupButtonPressed);
> 
> 		scale.Adjustment.Upper = 100.0;
> 		scale.Adjustment.Lower = 0.0;
> 		scale.DrawValue = false;
> 		scale.UpdatePolicy = UpdateType.Continuous;
> 		scale.Inverted = true;
> 
> 		scale.Show ();
> 
> 		label = new Label ("+");
> 		label.Show ();
> 		box.PackStart (label, false, true, 0);
> 
> 		label = new Label ("-");
> 		label.Show ();
> 		box.PackEnd (label, false, true, 0);
> 
> 		box.PackStart (scale, true, true, 0);
> 
> 		req = SizeRequest ();
> 
> 		GdkWindow.GetOrigin (out x, out y);
> 
> 		scale.SetSizeRequest (-1, 100);
> 		popup.SetSizeRequest (req.Width, -1);
> 
> 		popup.Move (x + Allocation.X, y + Allocation.Y + req.Height);
> 		popup.Show ();
> 
> 		popup.GrabFocus ();
> 
> 		Grab.Add (popup);
> 
> 		Gdk.GrabStatus grabbed = Gdk.Pointer.Grab (popup.GdkWindow, true, 
> 							   Gdk.EventMask.ButtonPressMask | Gdk.EventMask.ButtonReleaseMask | Gdk.EventMask.PointerMotionMask, 
> 							   null, null, 
> 							   CURRENT_TIME);
> 
> 		if (grabbed == Gdk.GrabStatus.Success) {
> 			grabbed = Gdk.Keyboard.Grab (popup.GdkWindow, true, CURRENT_TIME);
> 
> 			if (grabbed != Gdk.GrabStatus.Success) {
> 				Grab.Remove (popup);
> 				popup.Destroy ();
> 				popup = null;
> 			}
> 		} else {
> 			Grab.Remove (popup);
> 			popup.Destroy ();
> 			popup = null;
59,60c169,178
< 	public delegate void VolumeChangedHandler (int vol);
< 	public event VolumeChangedHandler VolumeChanged;
---
> 	private void HideScale ()
> 	{
> 		if (popup != null) {
> 			Grab.Remove (popup);
> 			Gdk.Pointer.Ungrab (CURRENT_TIME);
> 			Gdk.Keyboard.Ungrab (CURRENT_TIME);
> 
> 			popup.Destroy ();
> 			popup = null;
> 		}
62c180,181
< 	private delegate void SignalDelegate (IntPtr obj, int vol);
---
> 		Active = false;
> 	}
64c183
< 	private void VolumeChangedCallback (IntPtr obj, int vol)
---
> 	private void ToggleHandler (object obj, EventArgs args)
66,67c185,244
< 		if (VolumeChanged != null)
< 			VolumeChanged (vol);
---
> 		if (Active) {
> 			ShowScale ();
> 		}
> 		else {
> 			HideScale ();
> 		}
> 	}
> 
> 	private void ScrollHandler (object obj, ScrollEventArgs args)
> 	{
> 		int tmp_vol = Volume;
> 		
> 		switch (args.Event.Direction) {
> 		case Gdk.ScrollDirection.Up:
> 			tmp_vol += 10;
> 			break;
> 		case Gdk.ScrollDirection.Down:
> 			tmp_vol -= 10;
> 			break;
> 		default:
> 			break;
> 		}
> 
> 		// A CLAMP equiv doesn't seem to exist ... doing that manually
> 		tmp_vol = Math.Min (100, tmp_vol);
> 		tmp_vol = Math.Max (0, tmp_vol);
> 
> 		Volume = tmp_vol;
> 	}
> 
> 	private void ScaleValueChanged (object obj, EventArgs args)
> 	{
> 		Volume = (int)((VScale)obj).Value;
> 	}
> 
> 	private void ScaleKeyPressed (object obj, KeyPressEventArgs args)
> 	{
> 		switch (args.Event.Key) {
> 		case Gdk.Key.Escape:
> 			HideScale ();
> 			Volume = revert_volume;
> 			break;
> 		case Gdk.Key.KP_Enter:
> 		case Gdk.Key.ISO_Enter:
> 		case Gdk.Key.Key_3270_Enter:
> 		case Gdk.Key.Return:
> 		case Gdk.Key.space:
> 		case Gdk.Key.KP_Space:
> 			HideScale ();
> 			break;
> 		default:
> 			break;
> 		}
> 	}
> 
> 	private void PopupButtonPressed (object obj, ButtonPressEventArgs args)
> 	{
> 		if (popup != null) {
> 			HideScale ();
> 		}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]