1#!/usr/local/bin/perl -w
2################################################################################
3# xrdesktop2, a gtk-perl frontend for rdesktop                                 #
4# Copyright (C) 2002 David Kovach (xrdesktop 0.x-1.x)                          #
5# Copyright (C) 2014-2015 Peter TKATCHENKO, Chris Hutchinson                   #
6# Ported to Gtk2 by Peter TKATCHENKO, 2014                                     #
7# Maintained by Chris Hutchinson, 2015                                         #
8#                                                                              #
9# Redistribution and use in source and binary forms, with or without           #
10# modification, are permitted provided that the following conditions are met:  #
11#                                                                              #
12# 1) Redistributions of source code must retain the above copyright notice,    #
13# this list of conditions and the following disclaimer.                        #
14#                                                                              #
15# 2) Redistributions in binary form must reproduce the above copyright notice, #
16# this list of conditions and the following disclaimer in the documentation    #
17# and/or other materials provided with the distribution.                       #
18#                                                                              #
19# THIS SOFTWARE  IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" #
20# AND  ANY  EXPRESS  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE #
21# IMPLIED WARRANTIES  OF  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE #
22# ARE DISCLAIMED. IN  NO  EVENT  SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE #
23# LIABLE  FOR  ANY  DIRECT,  INDIRECT,  INCIDENTAL,  SPECIAL,  EXEMPLARY,  OR  #
24# CONSEQUENTIAL  DAMAGES  (INCLUDING,  BUT  NOT  LIMITED  TO,  PROCUREMENT  OF #
25# SUBSTITUTE  GOODS  OR  SERVICES;  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS #
26# INTERRUPTION)  HOWEVER CAUSED  AND  ON  ANY  THEORY OF LIABILITY, WHETHER IN #
27# CONTRACT,  STRICT  LIABILITY,  OR  TORT  (INCLUDING NEGLIGENCE OR OTHERWISE) #
28# ARISING  IN  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE #
29# POSSIBILITY OF SUCH DAMAGE.                                                  #
30################################################################################
31
32use Gtk2 -init;
33use Gtk2::SimpleList;
34use strict;
35use vars qw($ourname $VERSION);
36$ourname = qq(Xrdesktop2);
37#$VERSION = v2.00;
38$VERSION = qq(2.00);
39
40my $false = 0;
41my $true = 1;
42
43my $window;
44my $box1;
45my $scrolled_window1;
46my $buttonbox1;
47my $button1;
48my $button2;
49my $button3;
50my $button4;
51my $button5;
52my $list1;
53my %ip_hash;
54my %uname_hash;
55my %geom_hash;
56my %fullscreen_hash;
57my %bitmap_upd_hash;
58my %kmap_hash;
59my %license_hash;
60my %passthru_hash;
61my $machine_name;
62my $machine_ip;
63my $machine_uname;
64my $machine_geom;
65my $machine_fs;
66my $machine_bitmap_upd;
67my $machine_kmap;
68my $machine_lic;
69my $machine_passthru;
70my $raw_data;
71my $field_type;
72my $field_data;
73my $machina;
74my @machines;
75my @temp;
76my $selected_machine;
77my $selected_machine_ip;
78my $selected_uname;
79my $selected_geom;
80my $selected_fs;
81my $selected_bitmap_upd;
82my $selected_kmap;
83my $selected_kmap_hex;
84my $selected_lic;
85my $selected_passthru;
86my $dialog1;
87my $dialog_button_ok;
88my $dialog_button_cancel;
89my $name_entry;
90my $ip_entry;
91my $uname_entry;
92my $dialog2;
93my $combo1;
94my $combo2;
95my $fs_checkbox;
96my $bmp_checkbox;
97my $lic_checkbox;
98my $passthru_entry;
99my $passthru;
100my $indexmax=0;
101
102# the BIGASS list of keymaps. fear its awesome girth and stuff.
103## 02/10/2003 updated the list to work with rdesktop 1.2 and the
104## new way they do keyboard layouts...feh
105## left all the hex codes for which i could not find mappings as comments
106## in the hopes that someday it will get fixed.
107## also note that some of these are approximations and may or may
108## not work at all.
109my %kmap_trans_hash = (
110	"Albanian" => "en-us",
111#	"Arabic 101" => "0x00000401",
112#	"Arabic 102 AZERTY" => "0x00020401",
113#	"Arabic 102" => "0x00010401",
114#	"Armenian Eastern" => "0x0000042b",
115#	"Armenian Western" => "0x0001042b",
116#	"Azeri Cyrillic" => "0x0000082c",
117#	"Azeri Latin" => "0x0000042c",
118#	"Belarusian" => "0x00000423",
119#	"Belgian Comma" => "0x0001080c",
120	"Belgian Dutch" => "be",
121	"Belgian French" => "be",
122	"Bulgarian Latin" => "bg",
123#	"Bulgarian" => "0x00000402",
124	"Canadian French legacy" => "cf",
125	"Canadian French" => "en-us",
126#	"Canadian Multilingual Standard" => "0x00011009",
127#	"Chinese MS-PinYin98" => "0xe00e0804",
128#	"Chinese NeiMa" => "0xe0050804",
129#	"Chinese QuanPin" => "0xe0010804",
130#	"Chinese ShuangPin" => "0xe0020804",
131	"Chinese US Keyboard" => "ch",
132#	"Chinese ZhengMa" => "0xe0030804",
133#	"Chinese Alphanumeric" => "0xe01f0404",
134#	"Chinese Array" => "0xe0050404",
135#	"Chinese Big5 Code" => "0xe0040404",
136#	"Chinese ChangJie" => "0xe0020404",
137#	"Chinese DaYi" => "0xe0060404",
138#	"Chinese New ChangJie" => "0xe0090404",
139#	"Chinese New Phoneetic" => "0xe0080404",
140#	"Chinese Phonetic" => "0xe0010404",
141#	"Chinese Quick" => "0xe0030404",
142	"Chinese US Keyboard" => "ch",
143#	"Chinese Unicode" => "0xe0070404",
144#	"Croatian" => "0x0000041a",
145#	"Croatian Specific" => "0x0000041a",
146	"Czech QWERTY" => "cz",
147#	"Czech ProgrammerS" => "0x00020405",
148	"Czech" => "cz",
149	"Danish" => "dk",
150#	"Devanagari INSCRIPT" => "0x00000439",
151	"Dutch" => "nl",
152	"Estonian" => "et",
153#	"Faeroese" => "0x00000438",
154#	"Farsi" => "0x00000429",
155	"Finnish" => "su",
156	"French" => "fr",
157#	"Gaelic" => "0x00011809",
158#	"Georgian" => "0x00000437",
159# changed the German keymap string from "gr" to "de"
160# at the behest of Tobias Mohrluder. Thanks!
161# hopefully this is right :)
162	"German IBM" => "de",
163	"German" => "de",
164#	"Greek 220 Latin" => "0x00030408",
165	"Greek 220" => "gk",
166#	"Greek 319 Latin" => "0x00040408",
167	"Greek 319" => "gk",
168	"Greek Latin" => "gk",
169#	"Greek Polytonic" => "0x00060408",
170	"Greek" => "gk",
171#	"Hebrew" => "0x0000040d",
172#	"Hindi Traditional" => "0x00010439",
173	"Hungarian 101-key" => "hu",
174	"Hungarian" => "hu",
175	"Icelandic" => "is",
176	"Irish" => "en-us",
177	"Italian 142" => "it",
178	"Italian" => "it",
179#	"Japanese Input System" => "0xe0010411",
180	"Japanese" => "jp",
181#	"Japanese 106" => "0x00000411",
182#	"Japanese pc98x1" => "0x00000411",
183#	"Kazakh" => "0x0000043f",
184#	"Korean Hangul MS-IME98" => "0xe0010412",
185	"Korean Hangul" => "ko",
186	"Latin American" => "la",
187	"Latvian QWERTY" => "en-us",
188#	"Latvian" => "0x00000426",
189#	"Lithuanian IBM" => "0x00000427",
190#	"Lithuanian" => "0x00010427",
191#	"Macedonian FYROM" => "0x0000042f",
192#	"Marathi" => "0x0000044e",
193	"Norwegian" => "no",
194	"Polish 214" => "pl",
195	"Polish Programmers" => "pl",
196	"Portuguese Brazilian ABNT" => "br",
197#	"Portuguese Brazilian ABNT2" => "0x00010416",
198	"Portuguese" => "po",
199	"Romanian" => "ro",
200	"Russian Typewriter" => "ru",
201	"Russian" => "ru",
202#	"Russian KO18-R" => "0x00000419",
203	"Serbian Cyrillic" => "en-us",
204#	"Serbian Latin" => "0x0000081a",
205	"Slovak QWERTY" => "sl",
206	"Slovak" => "sl",
207#	"Slovak Programmers" => "0x0000041b",
208	"Slovenian" => "yu",
209#altered spanish keyboard courtesy of Jonay Gomez
210	"Spanish Variation" => "es",
211#	"Spanish" => "0x0000040a",
212	"Swedish" => "sv",
213	"Swiss French" => "sf",
214#	"Swiss German" => "0x0000080",
215#	"Tamil" => "0x00000449",
216#	"Tatar" => "0x00000444",
217#	"Thai Kedmanee non-shiftlock" => "0x0002041e",
218#	"Thai Kedmanee" => "0x0000041e",
219#	"Thai Pattachote non-shiftlock" => "0x0003041e",
220#	"Thai Pattachote" => "0x0001041e",
221	"Turkish F" => "tr",
222	"Turkish Q" => "tr",
223#	"US English Talbe for IBM Arabic 238_L" => "0x00050409",
224	"US" => "en-us",
225#	"US phantom key ver" => "0x00000409",
226#	"Ukranian" => "0x00000422",
227	"English" => "en-gb",
228	"US Dvorak left" => "usl",
229	"US Dvorak right" => "usr",
230	"US Dvorak" => "dv",
231	"US International" => "en-us",
232#	"Uzbek Cyrillic" => "0x00000843",
233#	"Vietnamese" => "0x0000042a",
234	"yu" => "yu",
235	"cf" => "cf",
236	"us9" => "en-us",
237);
238
239# create the window
240$window = Gtk2::Window->new;
241$window->signal_connect( "delete_event", sub { Gtk2->main_quit } );
242$window->set_default_size( 940, 600 );
243$window->set_border_width( 2 );
244$window->set_title("$ourname :: v$VERSION");
245
246# make our first box
247$box1 = Gtk2::VBox->new;
248$box1->set_spacing(8);
249
250# create the list, assign a signal to handle change of selection
251$list1 = Gtk2::SimpleList->new ("Name", "text", "Hostname/IP", "text", "Username", "text", "Keymap", "text", "Geometry", "text", "Fullscreen", "text", "Force Bitmap Updates", "text", "Do Not Request License", "text", "CMD Vars", "text" );
252
253# this is the scrolled window to put the List widget inside
254$scrolled_window1 = Gtk2::ScrolledWindow->new;
255$scrolled_window1->set_policy( "automatic", "automatic" );
256$scrolled_window1->add_with_viewport( $list1 );
257$box1->pack_start( $scrolled_window1, $true, $true, 0 );
258$window->add( $box1 );
259
260$list1->signal_connect( 'cursor-changed', \&list_sel_row );
261$list1->signal_connect( 'row_activated', \&rdesque_connect );
262
263# see the sub at the end of the script for info
264populate_clist_from_config();
265
266# Make some buttons
267$buttonbox1 = new Gtk2::HButtonBox();
268$button1 = new Gtk2::Button( "Connect" );
269$button2 = new Gtk2::Button( "Add" );
270$button3 = new Gtk2::Button( "Delete" );
271$button4 = new Gtk2::Button( "Edit" );
272$button5 = new Gtk2::Button( "Quit" );
273
274# tell them what to do
275$button1->signal_connect( "clicked", \&rdesque_connect );
276$button2->signal_connect( "clicked", \&rdesque_add );
277$button3->signal_connect( "clicked", \&rdesque_delete );
278$button4->signal_connect( "clicked", \&rdesque_edit );
279$button5->signal_connect( "clicked", sub { Gtk2->main_quit } );
280
281# put stuff in the window
282$box1->pack_start( $buttonbox1, $false, $false, 0 );
283$buttonbox1->add( $button1 );
284$buttonbox1->add( $button2 );
285$buttonbox1->add( $button3 );
286$buttonbox1->add( $button4 );
287$buttonbox1->add( $button5 );
288
289$window->show_all();
290
291main Gtk2;
292
293exit( 0 );
294
295# Subroutines
296
297# simple sub, to launch rdesktop with the configured info
298# when the connect button is clicked. rather tidy :)
299# remove the print line before the system call if you like quietness
300sub rdesque_connect
301{
302  my $buffer;
303# FIXME need to create man/pod page for these and add -q (quiet) switch
304  if( $selected_uname ) { $buffer = $buffer . " -u $selected_uname"; }
305  if( $selected_geom ) { $buffer = $buffer . " -g $selected_geom"; }
306  if( $selected_kmap_hex ) { $buffer = $buffer . " -k $selected_kmap_hex"; }
307  if( $selected_fs eq "true" ) { $buffer = $buffer . " -f"; }
308  if( $selected_bitmap_upd eq "true" ) { $buffer = $buffer . " -b"; }
309  if( $selected_lic eq "true" ) { $buffer = $buffer . " -l"; }
310  if( $selected_passthru ) { $buffer = $buffer . " $selected_passthru"; }
311
312#make the title whatever the string of the selected machine name is
313#instead of the machine's ip, courtesy of Jonay Gomez. thanks!
314#moved up here at the behest of Tobias Mohrluder. Thanks!
315  if( $selected_machine ) { $buffer = $buffer . " -T \"$selected_machine\""; }
316
317  if( $selected_machine_ip ) { $buffer = $buffer . " $selected_machine_ip"; }
318  else { die "No IP/Hostname selected! Aborting.\n"; }
319
320  print "commandline is \"rdesktop $buffer\"\n";
321  system("rdesktop $buffer \&");
322}
323
324# sub to add an entry into the list of machines to choose from
325# all this sub actually does is set up the dialog box to
326# collect the info, the do_add_server() sub is what
327# really does all the updating of info
328# yes i know the layout stuff is effin nasty
329# deal with it, i'm new at this gooey stuff... :P
330sub rdesque_add
331{
332 $dialog1 = new Gtk2::Dialog();
333 $dialog1->set_title("Add Server");
334 $dialog_button_ok = new Gtk2::Button( "Ok" );
335 $dialog_button_cancel = new Gtk2::Button( "Cancel" );
336 $dialog_button_ok->signal_connect( "clicked", \&do_add_server );
337 $dialog_button_cancel->signal_connect( "clicked", sub { $dialog1->destroy(); } );
338
339 $name_entry = new Gtk2::Entry( );
340 $ip_entry = new Gtk2::Entry( );
341 $uname_entry = new Gtk2::Entry( );
342 $passthru_entry = new Gtk2::Entry( );
343
344 my $label1 = new Gtk2::Label( "Enter a name for this server: " );
345 my $label2 = new Gtk2::Label( "Enter an IP address or hostname to connect to: " );
346 my $label3 = new Gtk2::Label( "Resolution: " );
347 my $label4 = new Gtk2::Label( "Username: " );
348 my $label5 = new Gtk2::Label( "Keyboard Mapping: " );
349 my $label6 = new Gtk2::Label( "Additional Variables to pass:                  " );
350
351 $label1->set_justify( "left" );
352 $label2->set_justify( "left" );
353 $label3->set_justify( "left" );
354 $label4->set_justify( "left" );
355 $label5->set_justify( "left" );
356 $label6->set_justify( "left" );
357
358 my $vbox1 = new Gtk2::VBox( $false, 0 );
359 my $vbox2 = new Gtk2::VBox( $false, 0 );
360 my $vbox3 = new Gtk2::VBox( $false, 0 );
361 my $vbox4 = new Gtk2::VBox( $false, 0 );
362 my $vbox5 = new Gtk2::VBox( $false, 0 );
363 my $vbox6 = new Gtk2::VBox( $false, 0 );
364 my $vbox7 = new Gtk2::VBox( $false, 0 );
365
366 my $hbox1 = new Gtk2::HBox( $false, 0 );
367 my $hbox2 = new Gtk2::HBox( $false, 0 );
368 my $hbox3 = new Gtk2::HBox( $false, 0 );
369 my $hbox4 = new Gtk2::HBox( $false, 0 );
370 my $hbox5 = new Gtk2::HBox( $false, 0 );
371 my $hbox6 = new Gtk2::HBox( $false, 0 );
372 my $hbox7 = new Gtk2::HBox( $false, 0 );
373
374 $combo1 = new Gtk2::Combo();
375 $combo2 = new Gtk2::Combo();
376 my @combo_list = ("640x480", "800x600", "1024x768", "1280x1024", "1400x1050", "1600x1200");
377 my @combo_list2 = sort keys %kmap_trans_hash;
378 $combo1->set_popdown_strings( @combo_list );
379 $combo1->set_use_arrows_always( $true );
380 $combo1->set_value_in_list( $false, $true );
381 $combo2->set_popdown_strings( @combo_list2 );
382 $combo2->set_use_arrows_always( $true );
383 $combo2->set_value_in_list( $true, $false );
384 # $combo2->entry->set_text( "US" );
385 $fs_checkbox = new Gtk2::CheckButton( "Fullscreen On" );
386 $bmp_checkbox = new Gtk2::CheckButton( "Force Bitmap Update" );
387 $lic_checkbox = new Gtk2::CheckButton( "Do Not Request License" );
388
389 $dialog1->vbox->set_homogeneous( $false );
390
391 $dialog1->vbox->pack_start( $hbox1, $false, $false, 0 );
392 $hbox1->pack_start( $vbox1, $false, $false, 0 );
393 $vbox1->pack_start( $label1, $false, $false, 0 );
394 $vbox1->pack_start( $name_entry, $false, $false, 0 );
395
396 $dialog1->vbox->pack_start( $hbox2, $false, $false, 0 );
397 $hbox2->pack_start( $vbox2, $false, $false, 0 );
398 $vbox2->pack_start( $label2, $false, $false, 0 );
399 $vbox2->pack_start( $ip_entry, $false, $false, 0 );
400
401 $dialog1->vbox->pack_start( $hbox4, $false, $false, 0 );
402 $hbox4->pack_start( $vbox4, $false, $false, 0 );
403 $vbox4->pack_start( $label4, $false, $false, 0 );
404 $vbox4->pack_start( $uname_entry, $false, $false, 0 );
405
406 $dialog1->vbox->pack_start( $hbox7, $false, $false, 0 );
407 $hbox7->pack_start( $vbox7, $false, $false, 0 );
408 $vbox7->pack_start( $label6, $false, $false, 0 );
409 $vbox7->pack_start( $passthru_entry, $false, $false, 0 );
410
411 $dialog1->vbox->pack_start( $hbox3, $false, $false, 0 );
412 $hbox3->pack_start( $vbox3, $false, $false, 0 );
413 $vbox3->pack_start( $label3, $false, $false, 0 );
414 $vbox3->pack_start( $combo1, $false, $false, 0 );
415
416 $dialog1->vbox->pack_start( $hbox5, $false, $false, 0 );
417 $hbox5->pack_start( $vbox5, $false, $false, 0 );
418 $vbox5->pack_start( $label5, $false, $false, 0 );
419 $vbox5->pack_start( $combo2, $false, $false, 0 );
420
421 $dialog1->vbox->pack_start( $hbox6, $false, $false, 0 );
422 $hbox6->pack_start( $vbox6, $false, $false, 0 );
423 $vbox6->pack_start( $fs_checkbox, $false, $false, 0 );
424 $vbox6->pack_start( $bmp_checkbox, $false, $false, 0 );
425 $vbox6->pack_start( $lic_checkbox, $false, $false, 0 );
426
427 $dialog1->action_area->pack_start( $dialog_button_ok, $true, $true, 0 );
428 $dialog1->action_area->pack_start( $dialog_button_cancel, $true, $true, 0 );
429
430 $name_entry->set_text( $selected_machine );
431 $ip_entry->set_text( $ip_hash{$selected_machine} );
432 $uname_entry->set_text( $uname_hash{$selected_machine} );
433 $passthru_entry->set_text( $passthru_hash{$selected_machine} );
434 $combo1->entry->set_text( $geom_hash{$selected_machine} );
435 $combo2->entry->set_text( $kmap_hash{$selected_machine} );
436 if( $fullscreen_hash{$selected_machine} eq "true")
437 {
438   $fs_checkbox->set_active( $true );
439 }
440 else { $fs_checkbox->set_active( $false ); }
441 if( $bitmap_upd_hash{$selected_machine} eq "true" )
442 {
443   $bmp_checkbox->set_active( $true );
444 }
445 else { $bmp_checkbox->set_active( $false ); }
446 if( $license_hash{$selected_machine} eq "true" )
447 {
448   $lic_checkbox->set_active( $true );
449 }
450 else { $lic_checkbox->set_active( $false ); }
451
452 $dialog1->show_all();
453}
454
455# remove a machine entry from the list
456sub rdesque_delete
457{
458  my $name_deleted;
459  my $ip_deleted;
460  $name_deleted = $selected_machine;
461  $ip_deleted = $ip_hash{$selected_machine};
462  delete $ip_hash{$selected_machine};
463  delete $uname_hash{$selected_machine};
464  delete $geom_hash{$selected_machine};
465  delete $fullscreen_hash{$selected_machine};
466  delete $bitmap_upd_hash{$selected_machine};
467  delete $kmap_hash{$selected_machine};
468  delete $license_hash{$selected_machine};
469  delete $passthru_hash{$selected_machine};
470  write_out_config();
471  print "Server deleted: name = $name_deleted, ip/hostname = $ip_deleted\n";
472  populate_clist_from_config();
473}
474
475# change an existing entry in the list
476# much like the rdesque_add() sub, all this does
477# set up a dialog to display the info and change it
478# all the real legwork is done by do_edit_server()
479# yes, the layout stuff is nasty, see comments for rdesque_add()
480sub rdesque_edit
481{
482  $dialog2 = new Gtk2::Dialog();
483  $dialog_button_ok = new Gtk2::Button( "Ok" );
484  $dialog_button_cancel = new Gtk2::Button( "Cancel" );
485  $dialog_button_ok->signal_connect( "clicked", \&do_edit_server );
486  $dialog_button_cancel->signal_connect( "clicked", sub { $dialog2->destroy(); } );
487
488  $name_entry = new Gtk2::Entry( );
489  $ip_entry = new Gtk2::Entry( );
490  $uname_entry = new Gtk2::Entry( );
491  $passthru_entry = new Gtk2::Entry( );
492
493  my $label1 = new Gtk2::Label( "Enter a name for this server: " );
494  my $label2 = new Gtk2::Label( "Enter an IP address or hostname to connect to: ");
495  my $label3 = new Gtk2::Label( "Resolution: " );
496  my $label4 = new Gtk2::Label( "Username: " );
497  my $label5 = new Gtk2::Label( "Keyboard Mapping: " );
498  my $label6 = new Gtk2::Label( "Additional Variables to pass:                  " );
499
500  $label1->set_justify( "left" );
501  $label2->set_justify( "left" );
502  $label3->set_justify( "left" );
503  $label4->set_justify( "left" );
504  $label5->set_justify( "left" );
505  $label6->set_justify( "left" );
506
507  my $vbox1 = new Gtk2::VBox( $false, 0 );
508  my $vbox2 = new Gtk2::VBox( $false, 0 );
509  my $vbox3 = new Gtk2::VBox( $false, 0 );
510  my $vbox4 = new Gtk2::VBox( $false, 0 );
511  my $vbox5 = new Gtk2::VBox( $false, 0 );
512  my $vbox6 = new Gtk2::VBox( $false, 0 );
513  my $vbox7 = new Gtk2::VBox( $false, 0 );
514
515  my $hbox1 = new Gtk2::HBox( $false, 0 );
516  my $hbox2 = new Gtk2::HBox( $false, 0 );
517  my $hbox3 = new Gtk2::HBox( $false, 0 );
518  my $hbox4 = new Gtk2::HBox( $false, 0 );
519  my $hbox5 = new Gtk2::HBox( $false, 0 );
520  my $hbox6 = new Gtk2::HBox( $false, 0 );
521  my $hbox7 = new Gtk2::HBox( $false, 0 );
522
523  $combo1 = new Gtk2::Combo();
524  $combo2 = new Gtk2::Combo();
525  my @combo_list = ("640x480", "800x600", "1024x768", "1280x1024", "1400x1050", "1600x1200");
526  my @combo_list2 = sort keys %kmap_trans_hash;
527  $combo1->set_popdown_strings( @combo_list );
528  $combo1->set_use_arrows_always( $true );
529  $combo1->set_value_in_list( $false, $true );
530  $combo2->set_popdown_strings( @combo_list2 );
531  $combo2->set_use_arrows_always( $true );
532  $combo2->set_value_in_list( $true, $false );
533
534  $fs_checkbox = new Gtk2::CheckButton( "Fullscreen On" );
535  $bmp_checkbox = new Gtk2::CheckButton( "Force Bitmap Update" );
536  $lic_checkbox = new Gtk2::CheckButton( "Do Not Request License" );
537
538  $dialog2->vbox->set_homogeneous( $false );
539
540  $dialog2->vbox->pack_start( $hbox1, $false, $false, 0 );
541  $hbox1->pack_start( $vbox1, $false, $false, 0 );
542  $vbox1->pack_start( $label1, $false, $false, 0 );
543  $vbox1->pack_start( $name_entry, $false, $false, 0 );
544
545  $dialog2->vbox->pack_start( $hbox2, $false, $false, 0 );
546  $hbox2->pack_start( $vbox2, $false, $false, 0 );
547  $vbox2->pack_start( $label2, $false, $false, 0 );
548  $vbox2->pack_start( $ip_entry, $false, $false, 0 );
549
550  $dialog2->vbox->pack_start( $hbox4, $false, $false, 0 );
551  $hbox4->pack_start( $vbox4, $false, $false, 0 );
552  $vbox4->pack_start( $label4, $false, $false, 0 );
553  $vbox4->pack_start( $uname_entry, $false, $false, 0 );
554
555  $dialog2->vbox->pack_start( $hbox7, $false, $false, 0 );
556  $hbox4->pack_start( $vbox7, $false, $false, 0 );
557  $vbox4->pack_start( $label6, $false, $false, 0 );
558  $vbox4->pack_start( $passthru_entry, $false, $false, 0 );
559
560  $dialog2->vbox->pack_start( $hbox3, $false, $false, 0 );
561  $hbox3->pack_start( $vbox3, $false, $false, 0 );
562  $vbox3->pack_start( $label3, $false, $false, 0 );
563  $vbox3->pack_start( $combo1, $false, $false, 0 );
564
565  $dialog2->vbox->pack_start( $hbox6, $false, $false, 0 );
566  $hbox6->pack_start( $vbox6, $false, $false, 0 );
567  $vbox6->pack_start( $label5, $false, $false, 0 );
568  $vbox6->pack_start( $combo2, $false, $false, 0 );
569
570  $dialog2->vbox->pack_start( $hbox5, $false, $false, 0 );
571  $hbox5->pack_start( $vbox5, $false, $false, 0 );
572  $vbox5->pack_start( $fs_checkbox, $false, $false, 0 );
573  $vbox5->pack_start( $bmp_checkbox, $false, $false, 0 );
574  $vbox5->pack_start( $lic_checkbox, $false, $false, 0 );
575
576  $dialog2->action_area->pack_start( $dialog_button_ok, $true, $true, 0 );
577  $dialog2->action_area->pack_start( $dialog_button_cancel, $true, $true, 0 );
578
579  $name_entry->set_text( $selected_machine );
580  $ip_entry->set_text( $ip_hash{$selected_machine} );
581  $uname_entry->set_text( $uname_hash{$selected_machine} );
582  $passthru_entry->set_text( $passthru_hash{$selected_machine} );
583  $combo1->entry->set_text( $geom_hash{$selected_machine} );
584  $combo2->entry->set_text( $kmap_hash{$selected_machine} );
585  if( $fullscreen_hash{$selected_machine} eq "true")
586  {
587    $fs_checkbox->set_active( $true );
588  }
589  else { $fs_checkbox->set_active( $false ); }
590  if( $bitmap_upd_hash{$selected_machine} eq "true" )
591  {
592    $bmp_checkbox->set_active( $true );
593  }
594  else { $bmp_checkbox->set_active( $false ); }
595  if( $license_hash{$selected_machine} eq "true" )
596  {
597    $lic_checkbox->set_active( $true );
598  }
599  else { $lic_checkbox->set_active( $false ); }
600
601  $dialog2->set_title("Edit Server: $selected_machine");
602
603  $dialog2->show_all();
604}
605
606# handler for when a row is selected, grabs info from the selected row
607# looks the corresponding values up in the hash and
608# stuffs them into variables to be used in other subs
609sub list_sel_row
610{
611  my $selection = ($list1->get_selected_indices)[0];
612  my @row = @{(@{$list1->{data}})[$selection]};
613
614  my $fs_tf;
615  my $bmp_tf;
616  $selected_machine = $row[0];
617  $selected_machine_ip = $ip_hash{$selected_machine};
618  $selected_uname = $uname_hash{$selected_machine};
619  $selected_geom = $geom_hash{$selected_machine};
620  $selected_fs = $fullscreen_hash{$selected_machine};
621  $selected_bitmap_upd = $bitmap_upd_hash{$selected_machine};
622  $selected_lic = $license_hash{$selected_machine};
623  $selected_kmap = $kmap_hash{$selected_machine};
624  $selected_kmap_hex = $kmap_trans_hash{$selected_kmap};
625  $selected_passthru = $passthru_hash{$selected_machine};
626
627  if( $selected_fs eq "true" ) { $fs_tf = "ON"; }
628  else { $fs_tf = "OFF"; }
629  if( $selected_bitmap_upd eq "true" ) { $bmp_tf = "Forcing Bitmap Updates"; }
630  else { $bmp_tf = "not Forcing Bitmap Updates"; }
631  print "You selected \"$selected_machine\", with the IP \"$selected_machine_ip\", Geometry is \"$selected_geom\"\nUsername is \"$selected_uname\", Fullscreen is $fs_tf, $bmp_tf\nKeyboard Mapping is \"$selected_kmap\"\n\n";
632}
633
634sub list_exec_row {
635  return 1 unless $selected_machine_ip;
636  my ($w, $ev) = @_;
637  if ($ev->{type} eq '2button_press' and $ev->{button} == 1) {
638      # well, probably we should emulate proper signal marshalling
639      # for 'click' on the 'Connect' button, but for the moment it's
640      # enough to simply call 'rdesque_connect'
641      # list double-click patch by Alexey Morozov
642      &rdesque_connect;
643  }
644  return 1;
645}
646
647# fairly simple, just spits out a config file based on the
648# data contained in the hashes
649sub write_out_config
650{
651  my $machina_p;
652  my @machines_p;
653  my $count = 1;
654
655  unlink( "$ENV{HOME}/.xrdesktop2_config" );
656  # fixed by Ken Caruso
657  open( CONFIG, ">$ENV{HOME}/.xrdesktop2_config" );
658  select( CONFIG );
659
660  print "\# NO USER SERVICABLE PARTS HERE.\n# EXPERTS ONLY!!!\n";
661  print "#\n\n";
662
663  @machines_p = keys %ip_hash;
664  foreach $machina_p (sort @machines_p)
665  {
666    print "# Entry $count\n";
667    print "Name=$machina_p\n";
668    print "IP=$ip_hash{$machina_p}\n";
669    print "Uname=$uname_hash{$machina_p}\n";
670    print "Geometry=$geom_hash{$machina_p}\n";
671    print "Fullscreen=$fullscreen_hash{$machina_p}\n";
672    print "Bitmap_Updates=$bitmap_upd_hash{$machina_p}\n";
673    print "Keymap=$kmap_hash{$machina_p}\n";
674    print "NoReqLic=$license_hash{$machina_p}\n";
675    print "Passthru=$passthru_hash{$machina_p}\n\n";
676    $count++;
677  }
678  select( STDOUT );
679  close( CONFIG );
680}
681
682# does the actual adding of the server to the hash and config files
683# since the rdesque_add() sub just creates the dialog.
684# this is the one linked to the "ok" button in the add dialog
685sub do_add_server
686{
687 my $name_added = $name_entry->get_text();
688 my $ip_added = $ip_entry->get_text();
689 my $uname_added = $uname_entry->get_text();
690 my $reso = $combo1->entry->get_text();
691 my $kmap = $combo2->entry->get_text();
692 my $pthru = $passthru_entry->get_text();
693 my $fs_state;
694 my $bmp_state;
695 my $lic_state;
696
697 if( $fs_checkbox->get_active ) {$fs_state = "true";}
698 else {$fs_state = "false";}
699
700 if( $bmp_checkbox->get_active ) {$bmp_state = "true";}
701 else {$bmp_state = "false";}
702
703 if( $lic_checkbox->get_active ) {$lic_state = "true";}
704 else {$lic_state = "false";}
705
706 chomp $name_added;
707 chomp $ip_added;
708 chomp $uname_added;
709 chomp $reso;
710 chomp $fs_state;
711 chomp $bmp_state;
712 chomp $lic_state;
713 chomp $kmap;
714 chomp $pthru;
715
716 my $check_dupe_key = exists $ip_hash{$name_added};
717 if( $name_added ne "" && $ip_added ne "" && $check_dupe_key != $true )
718 {
719   $ip_hash{$name_added} = $ip_added;
720   $uname_hash{$name_added} = $uname_added;
721   $geom_hash{$name_added} = $reso;
722   $fullscreen_hash{$name_added} = $fs_state;
723   $bitmap_upd_hash{$name_added} = $bmp_state;
724   $license_hash{$name_added} = $lic_state;
725   $kmap_hash{$name_added} = $kmap;
726   $passthru_hash{$name_added} = $pthru;
727#   print "Adding: $name_added, $pthru\n";
728   write_out_config();
729#   print "Added: name=$name_added, ip/hostname=$ip_added, username=$uname_added\nResolution=$reso, Fullscreen=$fs_state, Force Bitmap Updates=$bmp_state\nKeyboard Mapping=$kmap\n\n";
730   $dialog1->destroy();
731   populate_clist_from_config();
732 } else {
733   my $field1;
734   my $doh_dialog = new Gtk2::Dialog();
735   $doh_dialog->title("Err...");
736   my $doh_ok = new Gtk2::Button( "Ok" );
737   $doh_ok->signal_connect( "clicked", sub { $doh_dialog->destroy(); } );
738   my $doh_label = new Gtk2::Label();
739   if( $name_added eq "" )
740   {
741     $field1 = "You left the Name field blank.\nConfig NOT updated";
742     $doh_label->set_text( $field1 );
743   }
744   elsif( $ip_added eq "" )
745   {
746     $field1 = "You left the IP/Hostname field blank.\nConfig NOT updated";
747     $doh_label->set_text( $field1 );
748   }
749   elsif( $check_dupe_key )
750   {
751     $doh_label->set_text( "An entry named \"$name_added\" already exists.\nPlease choose another name." );
752   }
753   else
754   {
755     $field1 = "***CRITICAL ERROR***";
756     $doh_label->set_text( $field1 );
757   }
758   $doh_dialog->vbox->pack_start( $doh_label, $true, $true, 0 );
759   $doh_dialog->action_area->pack_start( $doh_ok, $true, $true, 0 );
760   $doh_dialog->show_all();
761 }
762}
763
764# does the actual editing of the server in the hash and config
765# files since the rdesque_edit() sub just creates the dialog.
766# this is the one linked to the "ok button in the dialog
767sub do_edit_server
768{
769  my $name_edited = $name_entry->get_text();
770  my $ip_edited = $ip_entry->get_text();
771  my $uname_edited = $uname_entry->get_text();
772  my $pthru = $passthru_entry->get_text();
773  my $reso = $combo1->entry->get_text();
774  my $kmap = $combo2->entry->get_text();
775  my $fs_state;
776  my $bmp_state;
777  my $lic_state;
778
779  if( $fs_checkbox->get_active ) {$fs_state = "true";}
780  else {$fs_state = "false";}
781
782  if( $bmp_checkbox->get_active ) {$bmp_state = "true";}
783  else {$bmp_state = "false";}
784
785  if( $lic_checkbox->get_active ) {$lic_state = "true";}
786  else {$lic_state = "false";}
787
788  chomp $name_edited;
789  chomp $ip_edited;
790  chomp $uname_edited;
791  chomp $reso;
792  chomp $fs_state;
793  chomp $bmp_state;
794  chomp $lic_state;
795  chomp $kmap;
796  chomp $pthru;
797
798  if( $name_edited ne "" && $ip_edited ne "" )
799  {
800    my $old_name = $selected_machine;
801    my $old_ip = $ip_hash{$selected_machine};
802
803    delete $ip_hash{$selected_machine};
804    $ip_hash{$name_edited} = $ip_edited;
805    delete $uname_hash{$selected_machine};
806    $uname_hash{$name_edited} = $uname_edited;
807    delete $geom_hash{$selected_machine};
808    $geom_hash{$name_edited} = $reso;
809    delete $fullscreen_hash{$selected_machine};
810    $fullscreen_hash{$name_edited} = $fs_state;
811    delete $bitmap_upd_hash{$selected_machine};
812    $bitmap_upd_hash{$name_edited} = $bmp_state;
813    delete $license_hash{$selected_machine};
814    $license_hash{$name_edited} = $lic_state;
815    delete $kmap_hash{$selected_machine};
816    $kmap_hash{$name_edited} = $kmap;
817    delete $passthru_hash{$selected_machine};
818    $passthru_hash{$name_edited} = $pthru;
819
820    write_out_config();
821    print "Old data: name = $old_name, ip/hostname = $old_ip\n";
822    print "New data: name = $name_edited, ip/hostname = $ip_edited\n";
823    $dialog2->destroy();
824    populate_clist_from_config();
825  } else {
826    my $field1;
827    my $doh_dialog = new Gtk2::Dialog();
828    $doh_dialog->title("Err...");
829    my $doh_ok = new Gtk2::Button( "Ok" );
830    $doh_ok->signal_connect( "clicked", sub { $doh_dialog->destroy(); } );
831    if( $name_edited eq "" ) { $field1 = "Name"; }
832    elsif( $ip_edited eq "" ) { $field1 = "IP/Hostname"; }
833    else { $field1 = "***CRITICAL ERROR***"; }
834    my $doh_label = new Gtk2::Label( "You left the $field1 field blank.\nConfig NOT updated." );
835
836    $doh_dialog->vbox->pack_start( $doh_label, $true, $true, 0 );
837    $doh_dialog->action_area->pack_start( $doh_ok, $true, $true, 0 );
838    $doh_dialog->show_all();
839  }
840}
841
842# reads in the config file (~/.xrdesktop_config by default)
843# plugs all the data into a hash ($ip_hash) and
844# sets up the $list1 widget with the data in the hash
845sub populate_clist_from_config
846{
847  unless( -e "$ENV{HOME}/.xrdesktop2_config") { write_out_config();}
848
849  open( CONFIG, "$ENV{HOME}/.xrdesktop2_config");
850  while( <CONFIG> )
851  {
852
853    # ignore comments and blank lines
854    next if /^#/;
855    next if /^$/;
856
857  # get me some data
858    $raw_data = $_;
859    chomp $raw_data;
860    ( $field_type, $field_data ) = split /=/, $raw_data,2;
861    chomp $field_type;
862    chomp $field_data;
863
864    # stuff the first line (hopefully Name) into vars
865    if( $field_type eq "Name" ) {$machine_name = $field_data;}
866    elsif( $field_type eq "IP" ) {$machine_ip = $field_data;}
867    elsif( $field_type eq "Uname" ) {$machine_uname = $field_data;}
868    elsif( $field_type eq "Geometry" ) { $machine_geom = $field_data;}
869    elsif( $field_type eq "Fullscreen" ) {$machine_fs = $field_data;}
870    elsif( $field_type eq "Bitmap_Updates" ) {$machine_bitmap_upd = $field_data;}
871    elsif( $field_type eq "Keymap" ) {$machine_kmap = $field_data;}
872    elsif( $field_type eq "NoReqLic" ) {$machine_lic = $field_data;}
873    elsif( $field_type eq "Passthru" ) {$machine_passthru = $field_data;}
874    else {print "Bad Data: $field_type : $field_data\n";}
875
876    # stuff the second line (hopefully IP) into vars
877    $raw_data = <CONFIG>;
878    chomp $raw_data;
879    ( $field_type, $field_data ) = split /=/, $raw_data,2;
880    chomp $field_type;
881    chomp $field_data;
882    if( $field_type eq "Name" ) {$machine_name = $field_data;}
883    elsif( $field_type eq "IP" ) {$machine_ip = $field_data;}
884    elsif( $field_type eq "Uname" ) {$machine_uname = $field_data;}
885    elsif( $field_type eq "Geometry" ) { $machine_geom = $field_data;}
886    elsif( $field_type eq "Fullscreen" ) {$machine_fs = $field_data;}
887    elsif( $field_type eq "Bitmap_Updates" ) {$machine_bitmap_upd = $field_data;}
888    elsif( $field_type eq "Keymap" ) {$machine_kmap = $field_data;}
889    elsif( $field_type eq "NoReqLic" ) {$machine_lic = $field_data;}
890    elsif( $field_type eq "Passthru" ) {$machine_passthru = $field_data;}
891    else {print "Bad Data: $field_type : $field_data\n";}
892
893    # stuff the third line (hopefully Uname) into vars
894    $raw_data = <CONFIG>;
895    chomp $raw_data;
896    ( $field_type, $field_data ) = split /=/, $raw_data,2;
897    chomp $field_type;
898    chomp $field_data;
899    if( $field_type eq "Name" ) {$machine_name = $field_data;}
900    elsif( $field_type eq "IP" ) {$machine_ip = $field_data;}
901    elsif( $field_type eq "Uname" ) {$machine_uname = $field_data;}
902    elsif( $field_type eq "Geometry" ) { $machine_geom = $field_data;}
903    elsif( $field_type eq "Fullscreen" ) {$machine_fs = $field_data;}
904    elsif( $field_type eq "Bitmap_Updates" ) {$machine_bitmap_upd = $field_data;}
905    elsif( $field_type eq "Keymap" ) {$machine_kmap = $field_data;}
906    elsif( $field_type eq "NoReqLic" ) {$machine_lic = $field_data;}
907    elsif( $field_type eq "Passthru" ) {$machine_passthru = $field_data;}
908    else {print "Bad Data: $field_type : $field_data\n";}
909
910    # stuff the fourth line (hopefully Geometry) into vars
911    $raw_data = <CONFIG>;
912    chomp $raw_data;
913    ( $field_type, $field_data ) = split /=/, $raw_data,2;
914    chomp $field_type;
915    chomp $field_data;
916    if( $field_type eq "Name" ) {$machine_name = $field_data;}
917    elsif( $field_type eq "IP" ) {$machine_ip = $field_data;}
918    elsif( $field_type eq "Uname" ) {$machine_uname = $field_data;}
919    elsif( $field_type eq "Geometry" ) { $machine_geom = $field_data;}
920    elsif( $field_type eq "Fullscreen" ) {$machine_fs = $field_data;}
921    elsif( $field_type eq "Bitmap_Updates" ) {$machine_bitmap_upd = $field_data;}
922    elsif( $field_type eq "Keymap" ) {$machine_kmap = $field_data;}
923    elsif( $field_type eq "NoReqLic" ) {$machine_lic = $field_data;}
924    elsif( $field_type eq "Passthru" ) {$machine_passthru = $field_data;}
925    else {print "Bad Data: $field_type : $field_data\n";}
926
927    # stuff the fifth line (hopefully Fullscreen) into vars
928    $raw_data = <CONFIG>;
929    chomp $raw_data;
930    ( $field_type, $field_data ) = split /=/, $raw_data,2;
931    chomp $field_type;
932    chomp $field_data;
933    if( $field_type eq "Name" ) {$machine_name = $field_data;}
934    elsif( $field_type eq "IP" ) {$machine_ip = $field_data;}
935    elsif( $field_type eq "Uname" ) {$machine_uname = $field_data;}
936    elsif( $field_type eq "Geometry" ) { $machine_geom = $field_data;}
937    elsif( $field_type eq "Fullscreen" ) {$machine_fs = $field_data;}
938    elsif( $field_type eq "Bitmap_Updates" ) {$machine_bitmap_upd = $field_data;}
939    elsif( $field_type eq "Keymap" ) {$machine_kmap = $field_data;}
940    elsif( $field_type eq "NoReqLic" ) {$machine_lic = $field_data;}
941    elsif( $field_type eq "Passthru" ) {$machine_passthru = $field_data;}
942    else {print "Bad Data: $field_type : $field_data\n";}
943
944    # stuff the sixth line (hopefully Bitmap_Updates) into vars
945    $raw_data = <CONFIG>;
946    chomp $raw_data;
947    ( $field_type, $field_data ) = split /=/, $raw_data,2;
948    chomp $field_type;
949    chomp $field_data;
950    if( $field_type eq "Name" ) {$machine_name = $field_data;}
951    elsif( $field_type eq "IP" ) {$machine_ip = $field_data;}
952    elsif( $field_type eq "Uname" ) {$machine_uname = $field_data;}
953    elsif( $field_type eq "Geometry" ) { $machine_geom = $field_data;}
954    elsif( $field_type eq "Fullscreen" ) {$machine_fs = $field_data;}
955    elsif( $field_type eq "Bitmap_Updates" ) {$machine_bitmap_upd = $field_data;}
956    elsif( $field_type eq "Keymap" ) {$machine_kmap = $field_data;}
957    elsif( $field_type eq "NoReqLic" ) {$machine_lic = $field_data;}
958    elsif( $field_type eq "Passthru" ) {$machine_passthru = $field_data;}
959    else {print "Bad Data: $field_type : $field_data\n";}
960
961    # stuff the seventh line (hopefully Keymap) into vars
962    $raw_data = <CONFIG>;
963    chomp $raw_data;
964    ( $field_type, $field_data ) = split /=/, $raw_data,2;
965    chomp $field_type;
966    chomp $field_data;
967    if( $field_type eq "Name" ) {$machine_name = $field_data;}
968    elsif( $field_type eq "IP" ) {$machine_ip = $field_data;}
969    elsif( $field_type eq "Uname" ) {$machine_uname = $field_data;}
970    elsif( $field_type eq "Geometry" ) { $machine_geom = $field_data;}
971    elsif( $field_type eq "Fullscreen" ) {$machine_fs = $field_data;}
972    elsif( $field_type eq "Bitmap_Updates" ) {$machine_bitmap_upd = $field_data;}
973    elsif( $field_type eq "Keymap" ) {$machine_kmap = $field_data;}
974    elsif( $field_type eq "NoReqLic" ) {$machine_lic = $field_data;}
975    elsif( $field_type eq "Passthru" ) {$machine_passthru = $field_data;}
976    else {print "Bad Data: $field_type : $field_data\n";}
977
978    # stuff the eighth line (hopefully NoReqLic) into vars
979    $raw_data = <CONFIG>;
980    chomp $raw_data;
981    ( $field_type, $field_data ) = split /=/, $raw_data,2;
982    chomp $field_type;
983    chomp $field_data;
984    if( $field_type eq "Name" ) {$machine_name = $field_data;}
985    elsif( $field_type eq "IP" ) {$machine_ip = $field_data;}
986    elsif( $field_type eq "Uname" ) {$machine_uname = $field_data;}
987    elsif( $field_type eq "Geometry" ) { $machine_geom = $field_data;}
988    elsif( $field_type eq "Fullscreen" ) {$machine_fs = $field_data;}
989    elsif( $field_type eq "Bitmap_Updates" ) {$machine_bitmap_upd = $field_data;}
990    elsif( $field_type eq "Keymap" ) {$machine_kmap = $field_data;}
991    elsif( $field_type eq "NoReqLic" ) {$machine_lic = $field_data;}
992    elsif( $field_type eq "Passthru" ) {$machine_passthru = $field_data;}
993    else {print "Bad Data: $field_type : $field_data\n";}
994
995    # stuff the ninth line (hopefully Passthru) into vars
996    $raw_data = <CONFIG>;
997    chomp $raw_data;
998    ( $field_type, $field_data ) = split /=/, $raw_data,2;
999    chomp $field_type;
1000    chomp $field_data;
1001    if( $field_type eq "Name" ) {$machine_name = $field_data;}
1002    elsif( $field_type eq "IP" ) {$machine_ip = $field_data;}
1003    elsif( $field_type eq "Uname" ) {$machine_uname = $field_data;}
1004    elsif( $field_type eq "Geometry" ) { $machine_geom = $field_data;}
1005    elsif( $field_type eq "Fullscreen" ) {$machine_fs = $field_data;}
1006    elsif( $field_type eq "Bitmap_Updates" ) {$machine_bitmap_upd = $field_data;}
1007    elsif( $field_type eq "Keymap" ) {$machine_kmap = $field_data;}
1008    elsif( $field_type eq "NoReqLic" ) {$machine_lic = $field_data;}
1009    elsif( $field_type eq "Passthru" ) {$machine_passthru = $field_data;}
1010    else {print "Bad Data: $field_type : $field_data\n";}
1011
1012    # finally, stuff it all into the hashes
1013    chomp $machine_name;
1014    chomp $machine_ip;
1015    chomp $machine_uname;
1016    chomp $machine_geom;
1017    chomp $machine_fs;
1018    chomp $machine_bitmap_upd;
1019    chomp $machine_kmap;
1020    chomp $machine_passthru;
1021    $ip_hash{$machine_name} = $machine_ip;
1022    $uname_hash{$machine_name} = $machine_uname;
1023    $geom_hash{$machine_name} = $machine_geom;
1024    $fullscreen_hash{$machine_name} = $machine_fs;
1025    $bitmap_upd_hash{$machine_name} = $machine_bitmap_upd;
1026    $license_hash{$machine_name} = $machine_lic;
1027    $kmap_hash{$machine_name} = $machine_kmap;
1028    $passthru_hash{$machine_name} = $machine_passthru;
1029  }
1030  close CONFIG;
1031
1032  # and last, but not least, stuff it all into the clist
1033  for (my $n=$indexmax-1; $n>=0; $n--)
1034  {
1035  splice @{ $list1->{data} }, $n, 1;
1036  }
1037  @machines = keys %ip_hash;
1038  $indexmax = 0;
1039  foreach $machina (sort @machines)
1040  {
1041    push @{$list1->{data}}, [$machina, $ip_hash{$machina}, $uname_hash{$machina}, $kmap_hash{$machina}, $geom_hash{$machina}, $fullscreen_hash{$machina}, $bitmap_upd_hash{$machina}, $license_hash{$machina}, $passthru_hash{$machina} ];
1042    $indexmax++;
1043  }
1044}
1045