ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/bin/deliantra
(Generate patch)

Comparing deliantra/Deliantra-Client/bin/deliantra (file contents):
Revision 1.92 by root, Wed Jan 14 00:00:47 2009 UTC vs.
Revision 1.107 by root, Thu Apr 8 03:51:26 2010 UTC

32 Win32::GUI::SplashScreen::Done (1); 32 Win32::GUI::SplashScreen::Done (1);
33 }; 33 };
34 } 34 }
35} 35}
36 36
37use strict; 37use common::sense;
38use utf8;
39 38
40use Carp 'verbose'; 39use Carp 'verbose';
41 40
42# do things only needed for single-binary version (par) 41# do things only needed for single-binary version (par)
43BEGIN { 42BEGIN {
56 55
57 if ($^O eq "MSWin32") { 56 if ($^O eq "MSWin32") {
58 # pango is relocatable on win32 57 # pango is relocatable on win32
59 } else { 58 } else {
60 # OS X 59 # OS X
61 $ENV{FONTCONFIG_FILE} = "$root/fonts.conf"; # no effect??!?!
62 $ENV{FONTCONFIG_DIR} = $root; # no effect??!?!
63 $ENV{PANGO_RC_FILE} = "$root/pango.rc"; 60 $ENV{PANGO_RC_FILE} = "$root/pango.rc";
64 $ENV{DYLD_LIBRARY_PATH} = $root; 61 $ENV{DYLD_LIBRARY_PATH} = $root;
65 chdir $root; # for pango modules, maybe other things 62 chdir $root; # for pango modules, maybe other things
66 } 63 }
67 64
68 unshift @INC, $root; 65 unshift @INC, $root;
69 } 66 }
70} 67}
71 68
72# prepend private library directory 69# prepend private library directory and prepare env
73BEGIN { 70BEGIN {
74 for (grep !ref, @INC) { 71 for (grep !ref, @INC) {
75 my $path = "$_/Deliantra/Client/private"; 72 my $path = "$_/Deliantra/Client/private";
76 if (-d $path) { 73 if (-d $path) {
77 unshift @INC, $path; 74 unshift @INC, $path;
135 crash "CRASH/EV::DIED: $@" => 0; 132 crash "CRASH/EV::DIED: $@" => 0;
136 DC::fatal Carp::longmess $@; 133 DC::fatal Carp::longmess $@;
137}; 134};
138 135
139my $MAX_FPS = 60; 136my $MAX_FPS = 60;
137
138our $DEFAULT_SERVER = "gameserver.deliantra.net";
140 139
141our $META_SERVER = "http://metaserver.schmorp.de/current.json"; 140our $META_SERVER = "http://metaserver.schmorp.de/current.json";
142 141
143our $LAST_REFRESH; 142our $LAST_REFRESH;
144our $NOW; 143our $NOW;
541 sub audio_tab_update; 540 sub audio_tab_update;
542 audio_tab_update; 541 audio_tab_update;
543} 542}
544 543
545sub audio_shutdown { 544sub audio_shutdown {
545 if ($SDL_MIXER) {
546 DC::MixMusic::halt;
547 DC::Mix_AllocateChannels 0;
548 }
549
546 undef $MUSIC_PLAYER; 550 undef $MUSIC_PLAYER;
547 undef $MUSIC_PLAYING_META; 551 undef $MUSIC_PLAYING_META;
548 undef $MUSIC_PLAYING_DATA; 552 undef $MUSIC_PLAYING_DATA;
549 553
550 $MUSIC_WANT = []; 554 $MUSIC_WANT = [];
802 user => $PROFILE->{user}, 806 user => $PROFILE->{user},
803 pass => $PROFILE->{password}, 807 pass => $PROFILE->{password},
804 mapw => $mapw, 808 mapw => $mapw,
805 maph => $maph, 809 maph => $maph,
806 810
811 client => "deliantra",
807 client => "$DC::VERSION $] $^O", 812 clientversion => $DC::VERSION,
808 813
809 map_widget => $MAPWIDGET, 814 map_widget => $MAPWIDGET,
810 statusbox => $STATUSBOX, 815 statusbox => $STATUSBOX,
811 map => $MAP, 816 map => $MAP,
812 mapmap => $MAPMAP, 817 mapmap => $MAPMAP,
818 823
819 on_connect => sub { 824 on_connect => sub {
820 if ($_[0]) { 825 if ($_[0]) {
821 DC::lowdelay fileno $CONN->{fh}; 826 DC::lowdelay fileno $CONN->{fh};
822 827
823 status "login successful"; 828 status "successfully connected to the server";
824 } else { 829 } else {
825 undef $CONN; 830 undef $CONN;
826 status "unable to connect: $!"; 831 status "unable to connect: $!";
827 stop_game(); 832 stop_game();
828 } 833 }
830 ; 835 ;
831} 836}
832 837
833sub start_game { 838sub start_game {
834 status "logging in..."; 839 status "logging in...";
840
841 my $server = $PROFILE->{host} || $DEFAULT_SERVER;
842 my ($host, $port) = AnyEvent::Socket::parse_hostport $server, "deliantra=13327"
843 or return status "$server: unable to parse server address, try an empty field.";
835 844
836 $LOGIN_BUTTON->set_text ("Logout"); 845 $LOGIN_BUTTON->set_text ("Logout");
837 $SETUP_DIALOG->hide; 846 $SETUP_DIALOG->hide;
838
839 my ($host, $port) = AnyEvent::Socket::parse_hostport $PROFILE->{host}, "deliantra=13327";
840 847
841 $MAP = new DC::Map; 848 $MAP = new DC::Map;
842 849
843 # hack to make SURE we find the IP address all right 850 # hack to make SURE we find the IP address all right
844 # can be removed once AnyEvent::DNS is proven stable. 851 # can be removed once AnyEvent::DNS is proven stable.
845 if ($host eq "gameserver.deliantra.net") { 852 if ($host eq "gameserver.deliantra.net") {
846 AnyEvent::DNS::a "dnstest.deliantra.net", sub { 853 AnyEvent::DNS::a "dnstest.deliantra.net", sub {
847 if ($_[0] ne "80.101.114.108") { # Perl 854 if ($_[0] ne "80.101.114.108") { # Perl
855 status "dns failure, trying differently";
856 $host = eval { Socket::inet_ntoa Socket::inet_aton "gameserver.deliantra.net" };
857 unless (defined $host) {
848 status "dns failure, using hardcoded address"; 858 status "dns failure, using hardcoded address";
849 $host = "129.13.162.95"; 859 $host = "129.13.162.95";
860 }
850 } 861 }
851 862
852 dc_connect $host, $port; 863 dc_connect $host, $port;
853 }; 864 };
854 } else { 865 } else {
1023 . "If you have a very slow system, non-accelerated drivers or plain dislike smooth scrolling, " 1034 . "If you have a very slow system, non-accelerated drivers or plain dislike smooth scrolling, "
1024 . "then disable this option. Changes take effect immdiately.", 1035 . "then disable this option. Changes take effect immdiately.",
1025 on_changed => sub { my ($self, $value) = @_; $CFG->{smooth_movement} = $value; 0 } 1036 on_changed => sub { my ($self, $value) = @_; $CFG->{smooth_movement} = $value; 0 }
1026 ); 1037 );
1027 1038
1039 $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Smooth Transitions");
1040 $table->add_at (1, $row++, new DC::UI::CheckBox
1041 state => $CFG->{smooth_transitions},
1042 tooltip => "<b>Smooth Transitions</b> tries to blend the fog of war and lighting smoothly between updates. "
1043 . "If you have a very slow system, non-accelerated drivers or plain dislike smooth scrolling, "
1044 . "then disable this option. Requires Smooth Movement and OpenGL Multitexturing. Changes take effect immdiately.",
1045 on_changed => sub { my ($self, $value) = @_; $CFG->{smooth_transitions} = $value; 0 }
1046 );
1047
1048
1028 $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Map Scale"); 1049 $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Map Scale");
1029 $table->add_at (1, $row++, new DC::UI::Slider 1050 $table->add_at (1, $row++, new DC::UI::Slider
1030 range => [(log $CFG->{map_scale}) / (log 2), -3, 1, 0, 1], 1051 range => [(log $CFG->{map_scale}) / (log 2), -3, 1, 0, 1],
1031 tooltip => "Enlarge or shrink the displayed map. Changes are instant.", 1052 tooltip => "Enlarge or shrink the displayed map. Changes are instant.",
1032 on_changed => sub { my ($self, $value) = @_; $CFG->{map_scale} = 2 ** $value; 0 } 1053 on_changed => sub { my ($self, $value) = @_; $CFG->{map_scale} = 2 ** $value; 0 }
1044 $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Fog of War"); 1065 $table->add_at (0, $row, new DC::UI::Label align => 1, text => "Fog of War");
1045 $table->add_at (1, $row++, new DC::UI::CheckBox 1066 $table->add_at (1, $row++, new DC::UI::CheckBox
1046 state => $CFG->{fow_enable}, 1067 state => $CFG->{fow_enable},
1047 tooltip => "<b>Fog-of-War</b> marks areas that cannot be seen by the player. Changes are instant.", 1068 tooltip => "<b>Fog-of-War</b> marks areas that cannot be seen by the player. Changes are instant.",
1048 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_enable} = $value; 0 } 1069 on_changed => sub { my ($self, $value) = @_; $CFG->{fow_enable} = $value; 0 }
1070 );
1071
1072 $table->add_at (0, $row, new DC::UI::Label align => 1, text => "FoW Pattern");
1073 $table->add_at (1, $row++, new DC::UI::ImageButton
1074 tex => $DC::MapWidget::TEX_HIDDEN[$CFG->{fow_texture}],
1075 bg => [0.3, 0.3, 0.2],
1076 force_w => 64,
1077 force_h => 64,
1078 tooltip => "<b>Fog of War Pattern.</b> The pattern that is overlaid over areas hidden from view. Click to cycle through various alternatives. Changes are instant.",
1079 on_activate => sub {
1080 my ($self) = @_;
1081 $CFG->{fow_texture} = ($CFG->{fow_texture} + 1) % @DC::MapWidget::TEX_HIDDEN;
1082 $self->set_texture ($DC::MapWidget::TEX_HIDDEN[$CFG->{fow_texture}]);
1083 $MAPWIDGET->update;
1084 }
1049 ); 1085 );
1050 1086
1051 $table->add_at (0, $row, new DC::UI::Label align => 1, text => "FoW Intensity"); 1087 $table->add_at (0, $row, new DC::UI::Label align => 1, text => "FoW Intensity");
1052 $table->add_at (1, $row++, new DC::UI::Slider 1088 $table->add_at (1, $row++, new DC::UI::Slider
1053 range => [$CFG->{fow_intensity}, 0, 1, 0, 1 / 256], 1089 range => [$CFG->{fow_intensity}, 0, 1, 0, 1 / 256],
1105 1141
1106 my $text = !$freq 1142 my $text = !$freq
1107 ? "audio is off" 1143 ? "audio is off"
1108 : "audio is enabled\n" 1144 : "audio is enabled\n"
1109 . "frequency (Hz): $freq\n" 1145 . "frequency (Hz): $freq\n"
1110 . "channels: $chans"; 1146 . "channels: $chans\n"
1147 . "chunk decoders available: " . (join ", ", DC::MixChunk::decoders) . "\n"
1148 . "music decoders available: " . (join ", ", DC::MixMusic::decoders);
1111 1149
1112 $AUDIO_INFO->set_text ($text); 1150 $AUDIO_INFO->set_text ($text);
1113} 1151}
1114 1152
1115sub audio_setup { 1153sub audio_setup {
1635 child => (my $table = new DC::UI::Table expand => 1, col_expand => [0, 1]), 1673 child => (my $table = new DC::UI::Table expand => 1, col_expand => [0, 1]),
1636 ); 1674 );
1637 1675
1638 $table->add_at (0, 4, new DC::UI::Label align => 1, text => "Username"); 1676 $table->add_at (0, 4, new DC::UI::Label align => 1, text => "Username");
1639 $table->add_at (1, 4, new DC::UI::Entry 1677 $table->add_at (1, 4, new DC::UI::Entry
1640 text => $CFG->{profile}{default}{user}, 1678 text => $PROFILE->{user},
1641 tooltip => "The name of your character on the server. The name is case-sensitive!", 1679 tooltip => "The name of your character on the server. The name is case-sensitive!",
1642 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{user} = $value; 1 } 1680 on_changed => sub { my ($self, $value) = @_; $PROFILE->{user} = $value; 1 }
1643 ); 1681 );
1644 1682
1645 $table->add_at (0, 5, new DC::UI::Label align => 1, text => "Password"); 1683 $table->add_at (0, 5, new DC::UI::Label align => 1, text => "Password");
1646 $table->add_at (1, 5, new DC::UI::Entry 1684 $table->add_at (1, 5, new DC::UI::Entry
1647 text => $CFG->{profile}{default}{password}, 1685 text => $PROFILE->{password},
1648 hidden => 1, 1686 hidden => 1,
1649 tooltip => "The password for your character.", 1687 tooltip => "The password for your character.",
1650 on_changed => sub { my ($self, $value) = @_; $CFG->{profile}{default}{password} = $value; 1 } 1688 on_changed => sub { my ($self, $value) = @_; $PROFILE->{password} = $value; 1 }
1651 ); 1689 );
1652 1690
1653 $table->add_at (1, 11, $LOGIN_BUTTON = new DC::UI::Button 1691 $table->add_at (1, 11, $LOGIN_BUTTON = new DC::UI::Button
1654 expand => 1, 1692 expand => 1,
1655 text => "Login / Register", 1693 text => "Login / Register",
1691 $table->add_at (1, $row, my $vbox = new DC::UI::VBox); 1729 $table->add_at (1, $row, my $vbox = new DC::UI::VBox);
1692 1730
1693 $vbox->add ( 1731 $vbox->add (
1694 $HOST_ENTRY = new DC::UI::Entry 1732 $HOST_ENTRY = new DC::UI::Entry
1695 expand => 1, 1733 expand => 1,
1696 text => $CFG->{profile}{default}{host}, 1734 text => $PROFILE->{host},
1697 tooltip => "The hostname or ip address of the Deliantra server to connect to (e.g. <b>gameserver.deliantra.net</b>)", 1735 tooltip => "The hostname or ip address of the Deliantra server to connect to (e.g. <b>gameserver.deliantra.net</b>)",
1698 on_changed => sub { 1736 on_changed => sub {
1699 my ($self, $value) = @_; 1737 my ($self, $value) = @_;
1700 $CFG->{profile}{default}{host} = $value; 1738 $PROFILE->{host} = $value;
1701 1 1739 1
1702 } 1740 }
1703 ); 1741 );
1704 1742
1705 if (0) { #d# disabled 1743 if (0) { #d# disabled
1904 $r 1942 $r
1905} 1943}
1906 1944
1907my %SORT_ORDER = ( 1945my %SORT_ORDER = (
1908 type => sub { 1946 type => sub {
1947 use sort 'stable';
1909 sort { $a->{type} <=> $b->{type} or $a->{name} cmp $b->{name} } @_ 1948 sort { $a->{type} <=> $b->{type} or $a->{name} cmp $b->{name} } @_
1910 }, 1949 },
1911 mtime => sub { 1950 mtime => sub {
1951 use sort 'stable';
1912 my $NOW = time; 1952 my $NOW = time;
1913 sort { 1953 sort {
1914 my $atime = $a->{mtime} - $NOW; $atime = $atime < 5 * 60 ? int $atime / 60 : 6; 1954 my $atime = $a->{mtime} - $NOW; $atime = $atime < 5 * 60 ? int $atime / 60 : 6;
1915 my $btime = $b->{mtime} - $NOW; $btime = $btime < 5 * 60 ? int $btime / 60 : 6; 1955 my $btime = $b->{mtime} - $NOW; $btime = $btime < 5 * 60 ? int $btime / 60 : 6;
1916 1956
1917 ($a->{flags} & F_LOCKED) <=> ($b->{flags} & F_LOCKED) 1957 ($a->{flags} & F_LOCKED) <=> ($b->{flags} & F_LOCKED)
1918 or $btime <=> $atime 1958 or $btime <=> $atime
1919 or $a->{type} <=> $b->{type} 1959 or $a->{type} <=> $b->{type}
1920 } @_ 1960 } @_
1921 }, 1961 },
1922 weight => sub { sort { 1962 weight => sub {
1963 use sort 'stable';
1964 sort {
1923 $a->{weight} * ($a->{nrof} || 1) <=> $b->{weight} * ($b->{nrof} || 1) 1965 $a->{weight} * ($a->{nrof} || 1) <=> $b->{weight} * ($b->{nrof} || 1)
1924 or $a->{type} <=> $b->{type} 1966 or $a->{type} <=> $b->{type}
1925 } @_ }, 1967 } @_
1968 },
1926); 1969);
1927 1970
1928sub inventory_widget { 1971sub inventory_widget {
1929 my $hb = new DC::UI::HBox homogeneous => 1; 1972 my $hb = new DC::UI::HBox homogeneous => 1;
1930 1973
2561 2604
2562 $MODE_SLIDER->set_range ([$CFG->{sdl_mode}, 0, scalar @SDL_MODES, 1, 1]); 2605 $MODE_SLIDER->set_range ([$CFG->{sdl_mode}, 0, scalar @SDL_MODES, 1, 1]);
2563 $MODE_SLIDER->emit (changed => $CFG->{sdl_mode}); 2606 $MODE_SLIDER->emit (changed => $CFG->{sdl_mode});
2564 2607
2565 $CAVEAT_LABEL->set_text ("None :)"); 2608 $CAVEAT_LABEL->set_text ("None :)");
2609 $CAVEAT_LABEL->set_text ("Apple/NVIDIA Texture bug (slow)")
2610 if $DC::OpenGL::APPLE_NVIDIA_BUG;
2566 $CAVEAT_LABEL->set_text ("Software Rendering (very slow)") 2611 $CAVEAT_LABEL->set_text ("Software Rendering (very slow)")
2567 unless DC::SDL_GL_GetAttribute DC::SDL_GL_ACCELERATED_VISUAL; 2612 unless DC::SDL_GL_GetAttribute DC::SDL_GL_ACCELERATED_VISUAL;
2568 2613
2569 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]); 2614 $STATUSBOX->add ("Set video mode $WIDTH×$HEIGHT", timeout => 10, fg => [1, 1, 1, 0.5]);
2570} 2615}
2703 fullscreen => 1, 2748 fullscreen => 1,
2704 fast => 0, 2749 fast => 0,
2705 force_opengl11 => undef, 2750 force_opengl11 => undef,
2706 disable_alpha => 0, 2751 disable_alpha => 0,
2707 smooth_movement => 1, 2752 smooth_movement => 1,
2753 smooth_transitions => 1,
2708 texture_compression => 1, 2754 texture_compression => 1,
2709 map_scale => 1, 2755 map_scale => 1,
2710 fow_enable => 1, 2756 fow_enable => 1,
2711 fow_intensity => 0, 2757 fow_intensity => 0,
2758 fow_texture => 0,
2712 map_smoothing => 1, 2759 map_smoothing => 1,
2713 gui_fontsize => 1, 2760 gui_fontsize => 1,
2714 log_fontsize => 0.7, 2761 log_fontsize => 0.7,
2715 gauge_fontsize => 1, 2762 gauge_fontsize => 1,
2716 gauge_size => 0.35, 2763 gauge_size => 0.35,
2734 shift_fire_stop => 0, 2781 shift_fire_stop => 0,
2735 uitheme => "wood", 2782 uitheme => "wood",
2736 map_shift_x => -24, # arbitrary 2783 map_shift_x => -24, # arbitrary
2737 map_shift_y => +24, # arbitrary 2784 map_shift_y => +24, # arbitrary
2738 ); 2785 );
2739 2786
2740 while (my ($k, $v) = each %DEF_CFG) { 2787 while (my ($k, $v) = each %DEF_CFG) {
2741 $CFG->{$k} = $v unless exists $CFG->{$k}; 2788 $CFG->{$k} = $v unless exists $CFG->{$k};
2742 } 2789 }
2743 2790
2744 $CFG->{profile}{default}{host} ||= "gameserver.deliantra.net"; 2791 my @args = @ARGV;
2792
2793 my $profile = 'default';
2794
2795 for (my $i = 0; $i < @args; $i++) {
2796 if ($args[$i] =~ /^--?profile$/) {
2797 $profile = $args[$i + 1];
2798 splice @args, $i, 2, ();
2799 $i = 0;
2800 } elsif ($args[$i] =~ /^--?h/) {
2801 print STDERR "Usage: $0 [--profile name] [host [user [password]]]\n";
2802 exit 0;
2803 }
2804 }
2805
2806 $CFG->{profile}{$profile} ||= {};
2745 $PROFILE = $CFG->{profile}{default}; 2807 $PROFILE = $CFG->{profile}{$profile};
2808 $PROFILE->{host} ||= "gameserver.deliantra.net";
2809
2810 $PROFILE->{host} = $args[0] if @args > 0;
2811 $PROFILE->{user} = $args[1] if @args > 1;
2812 $PROFILE->{password} = $args[2] if @args > 2;
2746 2813
2747 # convert old bindings (only default profile matters) 2814 # convert old bindings (only default profile matters)
2748 if (my $bindings = delete $PROFILE->{bindings}) { 2815 if (my $bindings = delete $PROFILE->{bindings}) {
2749 while (my ($mod, $syms) = each %$bindings) { 2816 while (my ($mod, $syms) = each %$bindings) {
2750 while (my ($sym, $cmds) = each %$syms) { 2817 while (my ($sym, $cmds) = each %$syms) {
2756 } 2823 }
2757 } 2824 }
2758 2825
2759 sdl_init; 2826 sdl_init;
2760 2827
2828 $ENV{FONTCONFIG_FILE} = DC::find_rcfile "fonts/fonts.conf";
2829 $ENV{FONTCONFIG_DIR} = DC::find_rcfile "fonts";
2830
2761 { 2831 {
2762 my @fonts = map DC::find_rcfile "fonts/$_", qw( 2832 my @fonts = map DC::find_rcfile "fonts/$_", qw(
2763 DejaVuSans.ttf 2833 DejaVuSans.ttf
2764 DejaVuSansMono.ttf 2834 DejaVuSansMono.ttf
2765 DejaVuSans-Bold.ttf 2835 DejaVuSans-Bold.ttf
2766 DejaVuSansMono-Bold.ttf 2836 DejaVuSansMono-Bold.ttf
2767 DejaVuSans-Oblique.ttf 2837 DejaVuSans-Oblique.ttf
2768 DejaVuSansMono-Oblique.ttf 2838 DejaVuSansMono-Oblique.ttf
2769 DejaVuSans-BoldOblique.ttf 2839 DejaVuSans-BoldOblique.ttf
2770 DejaVuSansMono-BoldOblique.ttf 2840 DejaVuSansMono-BoldOblique.ttf
2841 mona.ttf
2771 ); 2842 );
2772 2843
2773 DC::add_font $_ for @fonts; 2844 DC::add_font $_ for @fonts;
2774 2845
2775 $FONT_PROP = new_from_file DC::Font $fonts[0]; 2846 $FONT_PROP = new_from_file DC::Font $fonts[0];
2776 $FONT_FIXED = new_from_file DC::Font $fonts[1]; 2847 $FONT_FIXED = new_from_file DC::Font $fonts[1];
2777 2848
2778 $FONT_PROP->make_default; 2849 $FONT_PROP->make_default;
2779 2850
2823 2894
2824deliantra - A Deliantra MORPG game client 2895deliantra - A Deliantra MORPG game client
2825 2896
2826=head1 SYNOPSIS 2897=head1 SYNOPSIS
2827 2898
2828Just run it - no commandline arguments are supported. 2899 deliantra [--profile name] [host [user [password]]]
2900 deliantra --help
2829 2901
2830=head1 USAGE 2902=head1 USAGE
2831 2903
2832deliantra utilises OpenGL for all UI elements and the game. It is supposed to 2904The deliantra client utilises OpenGL for all UI elements and the game. It
2833be used in fullscreen mode and interactively. 2905is supposed to be used in fullscreen mode and interactively.
2834 2906
2835=head1 DEBUGGING 2907=head1 DEBUGGING
2836
2837 2908
2838CFPLUS_DEBUG - environment variable 2909CFPLUS_DEBUG - environment variable
2839 2910
2840 1 draw borders around widgets 2911 1 draw borders around widgets
2841 2 add low-level widget info to tooltips 2912 2 add low-level widget info to tooltips

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines