1 /*
2 **  jstest-gtk - A graphical joystick tester
3 **  Copyright (C) 2009 Ingo Ruhnke <grumbel@gmail.com>
4 **
5 **  This program is free software: you can redistribute it and/or modify
6 **  it under the terms of the GNU General Public License as published by
7 **  the Free Software Foundation, either version 3 of the License, or
8 **  (at your option) any later version.
9 **
10 **  This program is distributed in the hope that it will be useful,
11 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 **  GNU General Public License for more details.
14 **
15 **  You should have received a copy of the GNU General Public License
16 **  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 
19 #include <sstream>
20 #include <iostream>
21 #include <gtkmm/label.h>
22 #include <gtkmm/stock.h>
23 #include <gtkmm/dialog.h>
24 #include <gtkmm/image.h>
25 
26 #include "main.hpp"
27 #include "joystick.hpp"
28 #include "button_widget.hpp"
29 #include "joystick_map_widget.hpp"
30 #include "joystick_calibration_widget.hpp"
31 #include "joystick_test_widget.hpp"
32 
JoystickTestWidget(JoystickGui & gui,Joystick & joystick_,bool simple_ui)33 JoystickTestWidget::JoystickTestWidget(JoystickGui& gui, Joystick& joystick_, bool simple_ui) :
34   Gtk::Window(),
35   m_gui(gui),
36   joystick(joystick_),
37   m_simple_ui(simple_ui),
38   label("<b>" + joystick.get_name() + "</b>\nDevice: " + joystick.get_filename(),
39         Gtk::ALIGN_START, Gtk::ALIGN_START),
40   axis_frame("Axes"),
41   button_frame("Buttons"),
42   mapping_button("Mapping"),
43   calibration_button("Calibration"),
44   close_button(Gtk::Stock::CLOSE),
45   buttonbox(),
46   stick1_widget(128, 128),
47   stick2_widget(128, 128),
48   stick3_widget(128, 128),
49   rudder_widget(128, 32),
50   throttle_widget(32, 128),
51   left_trigger_widget(32, 128, true),
52   right_trigger_widget(32, 128, true)
53 {
54   set_title(joystick_.get_name());
55   set_icon_from_file(Main::current()->get_data_directory() + "generic.png");
56   label.set_use_markup(true);
57 
58   label.set_selectable(true);
59 
60   axis_frame.set_border_width(5);
61   axis_table.set_border_width(5);
62   axis_table.set_spacings(5);
63   button_frame.set_border_width(5);
64   button_table.set_border_width(5);
65   button_table.set_spacings(8);
66   buttonbox.set_border_width(5);
67 
68   for(int i = 0; i < joystick.get_axis_count(); ++i)
69   {
70     std::ostringstream str;
71     str << "Axis " << i << ": ";
72     Gtk::Label& label = *Gtk::manage(new Gtk::Label(str.str()));
73 
74     Gtk::ProgressBar& progressbar = *Gtk::manage(new Gtk::ProgressBar());
75     progressbar.set_fraction(0.5);
76 
77     //Each column must have at most 10 axes
78 
79     int x = (i/10)*2;
80     int y = i%10;
81 
82     axis_table.attach(label, x, x+1, y, y+1, Gtk::SHRINK, Gtk::FILL);
83     axis_table.attach(progressbar, x+1, x+2, y, y+1, Gtk::FILL|Gtk::EXPAND, Gtk::EXPAND);
84 
85     axes.push_back(&progressbar);
86   }
87 
88   for(int i = 0; i < joystick.get_button_count(); ++i)
89   {
90     int x = i / 10;
91     int y = i % 10;
92 
93     std::ostringstream str;
94     str << i;
95     ButtonWidget& button = *Gtk::manage(new ButtonWidget(32, 32, str.str()));
96     button_table.attach(button, x, x+1, y, y+1, Gtk::EXPAND, Gtk::EXPAND);
97     buttons.push_back(&button);
98   }
99 
100   alignment.set_padding(8, 8, 8, 8);
101   alignment.add(label);
102   m_vbox.pack_start(alignment, Gtk::PACK_SHRINK);
103 
104   buttonbox.add(mapping_button);
105   buttonbox.add(calibration_button);
106   buttonbox.add(close_button);
107 
108   test_hbox.pack_start(axis_frame,   Gtk::PACK_EXPAND_WIDGET);
109   test_hbox.pack_start(button_frame, Gtk::PACK_EXPAND_WIDGET);
110   m_vbox.pack_start(test_hbox, Gtk::PACK_SHRINK);
111   m_vbox.pack_end(buttonbox, Gtk::PACK_SHRINK);
112 
113   add(m_vbox);
114 
115   stick_hbox.set_border_width(5);
116 
117   axis_callbacks.clear();
118   // Using manual loop instead of resize, as else all the signals
119   // would be clones of each other, instead of individual signals
120   for(int i = 0; i < (int)joystick.get_axis_count(); ++i)
121     axis_callbacks.push_back(sigc::signal<void, double>());
122 
123   switch(joystick.get_axis_count())
124   {
125   case 2: // Simple stick
126     stick_hbox.pack_start(stick1_widget, Gtk::PACK_EXPAND_PADDING);
127     axis_callbacks[0].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_x_axis));
128     axis_callbacks[1].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_y_axis));
129     break;
130 
131   case 6: // Flightstick
132   {
133     Gtk::Table& table = *Gtk::manage(new Gtk::Table(2, 2));
134 
135     table.attach(stick1_widget, 0, 1, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
136     table.attach(rudder_widget,   0, 1, 1, 2, Gtk::SHRINK, Gtk::SHRINK);
137     table.attach(throttle_widget, 1, 2, 0, 1, Gtk::SHRINK, Gtk::SHRINK);
138 
139     stick_hbox.pack_start(table, Gtk::PACK_EXPAND_PADDING);
140     stick_hbox.pack_start(stick3_widget, Gtk::PACK_EXPAND_PADDING);
141 
142     axis_callbacks[0].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_x_axis));
143     axis_callbacks[1].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_y_axis));
144     axis_callbacks[2].connect(sigc::mem_fun(rudder_widget, &RudderWidget::set_pos));
145     axis_callbacks[3].connect(sigc::mem_fun(throttle_widget, &ThrottleWidget::set_pos));
146     axis_callbacks[4].connect(sigc::mem_fun(stick3_widget, &AxisWidget::set_x_axis));
147     axis_callbacks[5].connect(sigc::mem_fun(stick3_widget, &AxisWidget::set_y_axis));
148     break;
149   }
150     /*
151    // Dual Analog Gamepad
152 
153     // FIXME: never reached as this is the same as Flightstick, no
154     // way to tell them apart from simple axis count
155     stick_hbox.pack_start(stick1_widget, Gtk::PACK_EXPAND_PADDING);
156     stick_hbox.pack_start(stick2_widget, Gtk::PACK_EXPAND_PADDING);
157     stick_hbox.pack_start(stick3_widget, Gtk::PACK_EXPAND_PADDING);
158 
159     axis_callbacks[0].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_x_axis));
160     axis_callbacks[1].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_y_axis));
161     axis_callbacks[2].connect(sigc::mem_fun(stick2_widget, &AxisWidget::set_x_axis));
162     axis_callbacks[3].connect(sigc::mem_fun(stick2_widget, &AxisWidget::set_y_axis));
163     axis_callbacks[4].connect(sigc::mem_fun(stick3_widget, &AxisWidget::set_x_axis));
164     axis_callbacks[5].connect(sigc::mem_fun(stick3_widget, &AxisWidget::set_y_axis));
165     */
166 
167   case 8: // Dual Analog Gamepad + Analog Trigger
168     stick_hbox.pack_start(stick1_widget, Gtk::PACK_EXPAND_PADDING);
169     stick_hbox.pack_start(stick2_widget, Gtk::PACK_EXPAND_PADDING);
170     stick_hbox.pack_start(stick3_widget, Gtk::PACK_EXPAND_PADDING);
171     stick_hbox.pack_start(left_trigger_widget, Gtk::PACK_EXPAND_PADDING);
172     stick_hbox.pack_start(right_trigger_widget, Gtk::PACK_EXPAND_PADDING);
173 
174     axis_callbacks[0].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_x_axis));
175     axis_callbacks[1].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_y_axis));
176     axis_callbacks[2].connect(sigc::mem_fun(stick2_widget, &AxisWidget::set_x_axis));
177     axis_callbacks[3].connect(sigc::mem_fun(stick2_widget, &AxisWidget::set_y_axis));
178     axis_callbacks[6].connect(sigc::mem_fun(stick3_widget, &AxisWidget::set_x_axis));
179     axis_callbacks[7].connect(sigc::mem_fun(stick3_widget, &AxisWidget::set_y_axis));
180     axis_callbacks[4].connect(sigc::mem_fun(left_trigger_widget, &ThrottleWidget::set_pos));
181     axis_callbacks[5].connect(sigc::mem_fun(right_trigger_widget, &ThrottleWidget::set_pos));
182     break;
183 
184 
185   case 7: // Dual Analog Gamepad DragonRise Inc. Generic USB Joystick
186     stick_hbox.pack_start(stick1_widget, Gtk::PACK_EXPAND_PADDING);
187     stick_hbox.pack_start(stick2_widget, Gtk::PACK_EXPAND_PADDING);
188     stick_hbox.pack_start(stick3_widget, Gtk::PACK_EXPAND_PADDING);
189 
190     axis_callbacks[0].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_x_axis));
191     axis_callbacks[1].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_y_axis));
192     axis_callbacks[3].connect(sigc::mem_fun(stick2_widget, &AxisWidget::set_x_axis));
193     axis_callbacks[4].connect(sigc::mem_fun(stick2_widget, &AxisWidget::set_y_axis));
194     axis_callbacks[5].connect(sigc::mem_fun(stick3_widget, &AxisWidget::set_x_axis));
195     axis_callbacks[6].connect(sigc::mem_fun(stick3_widget, &AxisWidget::set_y_axis));
196     break;
197 
198   case 27: // Playstation 3 Controller
199     stick_hbox.pack_start(stick1_widget, Gtk::PACK_EXPAND_PADDING);
200     stick_hbox.pack_start(stick2_widget, Gtk::PACK_EXPAND_PADDING);
201     // Not using stick3 for now, as the dpad is 4 axis on the PS3, not 2 (one for each direction)
202     //stick_hbox.pack_start(stick3_widget, Gtk::PACK_EXPAND_PADDING);
203     stick_hbox.pack_start(left_trigger_widget, Gtk::PACK_EXPAND_PADDING);
204     stick_hbox.pack_start(right_trigger_widget, Gtk::PACK_EXPAND_PADDING);
205 
206     axis_callbacks[0].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_x_axis));
207     axis_callbacks[1].connect(sigc::mem_fun(stick1_widget, &AxisWidget::set_y_axis));
208     axis_callbacks[2].connect(sigc::mem_fun(stick2_widget, &AxisWidget::set_x_axis));
209     axis_callbacks[3].connect(sigc::mem_fun(stick2_widget, &AxisWidget::set_y_axis));
210     //axis_callbacks[6].connect(sigc::mem_fun(stick3_widget, &AxisWidget::set_x_axis));
211     //axis_callbacks[7].connect(sigc::mem_fun(stick3_widget, &AxisWidget::set_y_axis));
212     axis_callbacks[12].connect(sigc::mem_fun(left_trigger_widget, &ThrottleWidget::set_pos));
213     axis_callbacks[13].connect(sigc::mem_fun(right_trigger_widget, &ThrottleWidget::set_pos));
214     break;
215 
216   default:
217     std::cout << "Warning: unknown joystick, not displaying graphical representation." << std::endl;
218   }
219 
220   if (!m_simple_ui)
221   {
222     axis_vbox.pack_start(stick_hbox, Gtk::PACK_SHRINK);
223   }
224 
225   axis_vbox.add(axis_table);
226   axis_frame.add(axis_vbox);
227 
228   button_frame.add(button_table);
229 
230   joystick.axis_move.connect(sigc::mem_fun(this, &JoystickTestWidget::axis_move));
231   joystick.button_move.connect(sigc::mem_fun(this, &JoystickTestWidget::button_move));
232 
233   calibration_button.signal_clicked().connect(sigc::mem_fun(this, &JoystickTestWidget::on_calibrate));
234   mapping_button.signal_clicked().connect(sigc::mem_fun(this, &JoystickTestWidget::on_mapping));
235   close_button.signal_clicked().connect([this]{ hide(); });
236 
237   close_button.grab_focus();
238 }
239 
240 void
axis_move(int number,int value)241 JoystickTestWidget::axis_move(int number, int value)
242 {
243   axes.at(number)->set_fraction((value + 32767) / (double)(2*32767));
244 
245   std::ostringstream str;
246   str << value;
247   axes.at(number)->set_text(str.str());
248   axis_callbacks[number](value / 32767.0);
249 }
250 
251 void
button_move(int number,bool value)252 JoystickTestWidget::button_move(int number, bool value)
253 {
254   if (value)
255     buttons.at(number)->set_down(true);
256   else
257     buttons.at(number)->set_down(false);
258 }
259 
260 void
on_calibrate()261 JoystickTestWidget::on_calibrate()
262 {
263   m_gui.show_calibration_dialog();
264 }
265 
266 void
on_mapping()267 JoystickTestWidget::on_mapping()
268 {
269   m_gui.show_mapping_dialog();
270 }
271 
272 /* EOF */
273