Canola  0.8.D001
canola/window/card_reader.cc
Go to the documentation of this file.
00001 //
00002 // canola - canon canola 1614p emulator
00003 // Copyright (C) 2012 Peter Miller
00004 //
00005 // This program is free software; you can redistribute it and/or modify
00006 // it under the terms of the GNU General Public License, version 3, as
00007 // published by the Free Software Foundation.
00008 //
00009 // This program is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012 // General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License along
00015 // with this program. If not, see <http://www.gnu.org/licenses/>.
00016 //
00017 
00018 #include <lib/config.h>
00019 #include <libexplain/gettimeofday.h>
00020 
00021 #include <lib/program_card/functor/image/pixbuf.h>
00022 
00023 #include <canola/calculator/gnome.h>
00024 #include <canola/window/card_reader.h>
00025 
00026 
00027 window_card_reader::~window_card_reader()
00028 {
00029 }
00030 
00031 
00032 window_card_reader::window_card_reader(calculator_gnome *a_dst) :
00033     dst(*a_dst),
00034     pixels_per_second(100),
00035     pixels_per_instruction(1),
00036     current_address(0),
00037     current_card_number(0),
00038     currently_moving_left(false),
00039     current_card_started(0)
00040 {
00041     set_transient_for(*a_dst);
00042     set_decorated(false);
00043     Glib::RefPtr<Gdk::Visual> vp = get_screen()->get_rgba_visual();
00044     if (vp)
00045     {
00046         set_app_paintable();
00047         gtk_widget_set_visual(Gtk::Widget::gobj(), vp->gobj());
00048     }
00049     else
00050     {
00051         fprintf(stderr, "%s: %d: transparency not available\n", __FILE__,
00052             __LINE__);
00053     }
00054     add(layout);
00055 }
00056 
00057 
00058 window_card_reader::pointer
00059 window_card_reader::create(calculator_gnome *a_dst)
00060 {
00061     return pointer(new window_card_reader(a_dst));
00062 }
00063 
00064 
00065 static double
00066 get_time_of_day(void)
00067 {
00068     timeval result;
00069     explain_gettimeofday_or_die(&result, 0);
00070     return (result.tv_usec * 1e-6 + result.tv_sec);
00071 }
00072 
00073 
00074 static int
00075 get_position_x(int card_num)
00076 {
00077     return (card_num * 30);
00078 }
00079 
00080 
00081 static int
00082 get_position_y(int card_num)
00083 {
00084     return (card_num * 10);
00085 }
00086 
00087 
00088 bool
00089 window_card_reader::start(const std::string &src, bool binary)
00090 {
00091     //
00092     // Load the card set into the proxy.  We use a proxy for two
00093     // reasons:
00094     // 1. We have to dole out the instrinctions on at a time, as the
00095     //    animation progresses, so as to emulate the reading of the
00096     //    opcode by the original optical reader.
00097     // 2. We have to store them as a "program" even in the case where
00098     //    the cards are a data set for one of the General Function card
00099     //    sets.
00100     //
00101     dst.program_mode_set(calculator::program_mode_learn);
00102     dst.clear_program();
00103     if (!dst.load_program(src, binary))
00104         return false;
00105 
00106     //
00107     // Now that we have the program, turn it into a set of images in pixbufs.
00108     // It is these pixbufs that can be displayed by Gtk::Image wigdets.
00109     //
00110     double dpi = get_screen()->get_resolution();
00111     card_pixbufs.clear();
00112     program_card_functor_image_pixbuf::pointer ffs =
00113         program_card_functor_image_pixbuf::create(card_pixbufs, dpi);
00114     ffs->set_disassemble(true);
00115     dst.program_card_walk(*ffs);
00116     ffs.reset();
00117     if (card_pixbufs.empty())
00118         return false;
00119 
00120     //
00121     // Calculate how fast the card will have to travel under the
00122     // simulated optical reader at the top of the window.
00123     //
00124     pixels_per_second = card_pixbufs[0]->get_height() / 4.;
00125     pixels_per_instruction = card_pixbufs[0]->get_height() / 40;
00126     assert(pixels_per_instruction >= 1);
00127 
00128     //
00129     // For each of the pixbufs, create a Gtk::Image widget to display
00130     // it.  We have to do this in reverse, so that the first card is the
00131     // last widget, and comes out on top of the visable stack of cards.
00132     //
00133     card_image_widgets.clear();
00134     for (size_t j = 0; j < card_pixbufs.size(); ++j)
00135     {
00136         size_t jj = card_pixbufs.size() - 1 - j;
00137         pixbuf_ptr_t pp = card_pixbufs[jj];
00138         assert(pp);
00139         if (pp)
00140         {
00141             gtk_image_ptr_t p(new Gtk::Image(pp));
00142             card_image_widgets.push_back(p);
00143             layout.put(*p, get_position_x(jj), get_position_y(jj));
00144         }
00145     }
00146 
00147     //
00148     // Show the window, and its card images, to the user.
00149     //
00150     assert(card_image_widgets.size() == card_pixbufs.size());
00151     if (is_composited())
00152     {
00153         layout.hide();
00154         show();
00155         Gdk::RGBA glass;
00156         glass.set_rgba(1., 1., 1., 0.);
00157         assert(get_window());
00158         get_window()->set_background(glass);
00159     }
00160     show_all_children();
00161 
00162     current_card_number = 0;
00163     currently_moving_left = false;
00164     current_card_started = get_time_of_day();
00165     current_address = 1;
00166 
00167     //
00168     // Start the animation timer running.
00169     //
00170     animation_timer =
00171         Glib::signal_timeout().connect
00172         (
00173             sigc::mem_fun(*this, &window_card_reader::on_animation_timer),
00174             1000 / 24
00175         );
00176     return true;
00177 }
00178 
00179 
00180 bool
00181 window_card_reader::empty(void)
00182     const
00183 {
00184     return card_image_widgets.empty();
00185 }
00186 
00187 
00188 bool
00189 window_card_reader::on_animation_timer(void)
00190 {
00191     if (current_card_number >= card_image_widgets.size())
00192     {
00193         done();
00194         return false;
00195     }
00196 
00197     int card_height = card_pixbufs[0]->get_height();
00198     double now = get_time_of_day();
00199     double t = now - current_card_started;
00200     int jj = card_image_widgets.size() - 1 - current_card_number;
00201     if (currently_moving_left)
00202     {
00203         int x0 = get_position_x(current_card_number);
00204         int y = get_position_y(current_card_number);
00205         int x = x0 - t * pixels_per_second;
00206         if (x <= 0)
00207         {
00208             x = 0;
00209             currently_moving_left = false;
00210             current_card_started = now;
00211         }
00212         layout.move(*card_image_widgets[jj], x, y);
00213     }
00214     else
00215     {
00216         int x = 0;
00217         int y0 = get_position_y(current_card_number);
00218         int yz = -card_height;
00219         int y = y0 - t * pixels_per_second;
00220         layout.move(*card_image_widgets[jj], x, y);
00221         if (y < 0)
00222         {
00223             int new_addr = -y / pixels_per_instruction;
00224             if (new_addr < 40)
00225             {
00226                 new_addr += current_card_number * 40;
00227                 ++new_addr;
00228                 while (current_address <= new_addr)
00229                 {
00230                     dst.forward(current_address);
00231                     ++current_address;
00232                 }
00233             }
00234         }
00235         if (y < yz)
00236         {
00237             assert((current_address % 40) == 1);
00238             ++current_card_number;
00239             currently_moving_left = true;
00240             current_card_started = now;
00241         }
00242     }
00243     return true;
00244 }
00245 
00246 
00247 void
00248 window_card_reader::done(void)
00249 {
00250     //
00251     // No need for the time any more.
00252     //
00253     animation_timer.disconnect();
00254 
00255     //
00256     // Hide the window.
00257     //
00258     hide();
00259 
00260     //
00261     // Remove the image widgets from the layout.
00262     //
00263     while (!card_image_widgets.empty())
00264     {
00265         gtk_image_ptr_t wp = card_image_widgets.back();
00266         card_image_widgets.pop_back();
00267         layout.remove(*wp);
00268     }
00269 
00270     //
00271     // Free the card images.
00272     //
00273     card_pixbufs.clear();
00274 }
00275 
00276 
00277 bool
00278 window_card_reader::is_busy(void)
00279 {
00280     return animation_timer.connected();
00281 }