Update to v106r69 release.

byuu says:

The biggest change was improving WonderSwan emulation. With help from
trap15, I tracked down a bug where I was checking the wrong bit for
reverse DMA transfers. Then I also emulated VTOTAL to support variable
refresh rate. Then I improved HyperVoice emulation which should be
unsigned samples in three of four modes. That got Fire Lancer running
great. I also rewrote the disassembler. The old one disassembled many
instructions completely wrong, and deviated too much from any known x86
syntax. I also emulated some of the quirks of the V30 (two-byte POP into
registers fails, SALC is just XLAT mirrored, etc) which probably don't
matter unless someone tries to run code to verify it's a NEC CPU and not
an Intel CPU, but hey, why not?

I also put more work into the MSX skeleton, but it's still just a
skeleton with no real emulation yet.
This commit is contained in:
Tim Allen
2019-01-02 10:52:08 +11:00
parent 3159285eaa
commit aaf094e7c4
115 changed files with 3319 additions and 1394 deletions

View File

@@ -10,6 +10,9 @@ auto pApplication::run() -> void {
while(!Application::state().quit) {
Application::doMain();
processEvents();
//avoid spinlooping the thread when there is no main loop ...
//when there is one, Application::onMain() is expected to sleep when possible instead
if(!Application::state().onMain) usleep(2000);
}
}
@@ -18,7 +21,13 @@ auto pApplication::pendingEvents() -> bool {
}
auto pApplication::processEvents() -> void {
while(pendingEvents()) gtk_main_iteration_do(false);
//GTK can sometimes return gtk_pending_events() == true forever,
//no matter how many times gtk_main_iteration_do() is called.
//implement a timeout to prevent hiro from hanging forever in this case.
auto time = chrono::millisecond();
while(pendingEvents() && chrono::millisecond() - time < 50) {
gtk_main_iteration_do(false);
}
for(auto& window : state().windows) window->_synchronizeGeometry();
}

View File

@@ -16,7 +16,8 @@ auto pFont::size(PangoFontDescription* font, const string& text) -> Size {
pango_layout_set_text(layout, text, -1);
int width = 0, height = 0;
pango_layout_get_pixel_size(layout, &width, &height);
g_object_unref((gpointer)layout);
g_object_unref(layout);
g_object_unref(context);
return {width, height};
}
@@ -29,7 +30,7 @@ auto pFont::family(const string& family) -> string {
#elif defined(DISPLAY_XORG)
if(family == Font::Sans ) return "Sans";
if(family == Font::Serif) return "Serif";
if(family == Font::Mono ) return "Liberation Mono";
if(family == Font::Mono ) return "Monospace";
return family ? family : "Sans";
#else
return family;

View File

@@ -12,6 +12,9 @@ auto pSizable::minimumSize() const -> Size {
return {0, 0};
}
auto pSizable::setCollapsible(bool collapsible) -> void {
}
auto pSizable::setGeometry(Geometry geometry) -> void {
self().doSize();
}

View File

@@ -6,6 +6,7 @@ struct pSizable : pObject {
Declare(Sizable, Object)
virtual auto minimumSize() const -> Size;
virtual auto setCollapsible(bool collapsible) -> void;
virtual auto setGeometry(Geometry geometry) -> void;
};

View File

@@ -3,6 +3,9 @@
namespace hiro {
static auto Timer_trigger(pTimer* p) -> signed {
//prevent all timers from firing once the program has been terminated
if(Application::state().quit) return false;
//timer may have been disabled prior to triggering, so check state
if(p->self().enabled(true)) p->self().doActivate();

View File

@@ -46,6 +46,24 @@ static auto Label_expose(GtkWidget* widget, GdkEvent* event, pLabel* p) -> int {
return false;
}
static auto Label_mousePress(GtkWidget* widget, GdkEventButton* event, pLabel* p) -> int {
switch(event->button) {
case 1: p->self().doMousePress(Mouse::Button::Left); break;
case 2: p->self().doMousePress(Mouse::Button::Middle); break;
case 3: p->self().doMousePress(Mouse::Button::Right); break;
}
return true;
}
static auto Label_mouseRelease(GtkWidget* widget, GdkEventButton* event, pLabel* p) -> int {
switch(event->button) {
case 1: p->self().doMouseRelease(Mouse::Button::Left); break;
case 2: p->self().doMouseRelease(Mouse::Button::Middle); break;
case 3: p->self().doMouseRelease(Mouse::Button::Right); break;
}
return true;
}
auto pLabel::construct() -> void {
gtkWidget = gtk_event_box_new();
subWidget = gtk_label_new("");
@@ -57,6 +75,8 @@ auto pLabel::construct() -> void {
setForegroundColor(state().foregroundColor);
setText(state().text);
g_signal_connect(G_OBJECT(gtkWidget), "button-press-event", G_CALLBACK(Label_mousePress), (gpointer)this);
g_signal_connect(G_OBJECT(gtkWidget), "button-release-event", G_CALLBACK(Label_mouseRelease), (gpointer)this);
#if HIRO_GTK==2
g_signal_connect(G_OBJECT(subWidget), "expose-event", G_CALLBACK(Label_expose), (gpointer)this);
#elif HIRO_GTK==3

View File

@@ -7,13 +7,7 @@ static auto SourceEdit_change(GtkTextBuffer*, pSourceEdit* p) -> void {
}
static auto SourceEdit_move(GObject*, GParamSpec*, pSourceEdit* p) -> void {
signed offset = 0;
g_object_get(G_OBJECT(p->gtkSourceBuffer), "cursor-position", &offset, nullptr);
if(p->state().cursor.offset() != offset) {
p->state().cursor.setOffset(offset);
if(!p->locked()) p->self().doMove();
}
if(!p->locked()) p->self().doMove();
}
auto pSourceEdit::construct() -> void {
@@ -24,16 +18,16 @@ auto pSourceEdit::construct() -> void {
gtk_scrolled_window_set_shadow_type(gtkScrolledWindow, GTK_SHADOW_ETCHED_IN);
gtkSourceLanguageManager = gtk_source_language_manager_get_default();
gtkSourceLanguage = gtk_source_language_manager_get_language(gtkSourceLanguageManager, "cpp");
gtkSourceLanguage = gtk_source_language_manager_get_language(gtkSourceLanguageManager, "");
gtkSourceStyleSchemeManager = gtk_source_style_scheme_manager_get_default();
gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, "oblivion");
gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, "classic");
gtkSourceBuffer = gtk_source_buffer_new(nullptr);
gtkTextBuffer = GTK_TEXT_BUFFER(gtkSourceBuffer);
gtk_source_buffer_set_highlight_matching_brackets(gtkSourceBuffer, true);
gtk_source_buffer_set_highlight_syntax(gtkSourceBuffer, true);
//gtk_source_buffer_set_language(gtkSourceBuffer, gtkSourceLanguage);
gtk_source_buffer_set_language(gtkSourceBuffer, gtkSourceLanguage);
gtk_source_buffer_set_style_scheme(gtkSourceBuffer, gtkSourceStyleScheme);
gtkSourceView = (GtkSourceView*)gtk_source_view_new_with_buffer(gtkSourceBuffer);
@@ -55,6 +49,9 @@ auto pSourceEdit::construct() -> void {
gtk_widget_show(gtkWidgetSourceView);
setEditable(state().editable);
setLanguage(state().language);
setNumbered(state().numbered);
setScheme(state().scheme);
setText(state().text);
setWordWrap(state().wordWrap);
@@ -70,6 +67,23 @@ auto pSourceEdit::destruct() -> void {
gtk_widget_destroy(gtkWidget);
}
auto pSourceEdit::cursor() const -> Cursor {
Cursor cursor;
int offset = 0;
g_object_get(G_OBJECT(gtkSourceBuffer), "cursor-position", &offset, nullptr);
cursor.setOffset(offset);
GtkTextIter start, end;
if(gtk_text_buffer_get_selection_bounds(gtkTextBuffer, &start, &end)) {
//if selecting text from left to right, the cursor may be ahead of the selection start ...
//since hiro combines selection bounds (end-start) into length, move the offset to the start
int origin = gtk_text_iter_get_offset(&start);
cursor.setOffset(origin);
int length = gtk_text_iter_get_offset(&end) - origin;
cursor.setLength(length);
}
return cursor;
}
auto pSourceEdit::setCursor(Cursor cursor) -> void {
lock();
GtkTextIter offset, length;
@@ -92,45 +106,33 @@ auto pSourceEdit::setFocused() -> void {
gtk_widget_grab_focus(gtkWidgetSourceView);
}
/*
auto pSourceEdit::setPosition(signed position) -> void {
lock();
GtkTextIter iter;
//note: iterators must be initialized via get_iter() before calling set_offset()
gtk_text_buffer_get_end_iter(gtkTextBuffer, &iter);
if(position >= 0) {
gtk_text_iter_set_offset(&iter, position);
} else {
state().position = gtk_text_iter_get_offset(&iter);
}
gtk_text_buffer_place_cursor(gtkTextBuffer, &iter);
auto mark = gtk_text_buffer_get_mark(gtkTextBuffer, "insert");
gtk_text_view_scroll_mark_onscreen(gtkTextView, mark);
unlock();
auto pSourceEdit::setLanguage(const string& language) -> void {
string name;
if(language == "C") name = "c";
if(language == "C++") name = "cpp";
if(language == "Make") name = "makefile";
gtkSourceLanguage = gtk_source_language_manager_get_language(gtkSourceLanguageManager, name);
gtk_source_buffer_set_language(gtkSourceBuffer, gtkSourceLanguage);
}
auto pSourceEdit::setSelected(Position selected) -> void {
lock();
GtkTextIter iter;
gtk_text_buffer_get_end_iter(gtkTextBuffer, &iter);
signed offset = gtk_text_iter_get_offset(&iter);
if(selected.x() < 0 || selected.x() > offset) selected.setX(offset);
if(selected.y() < 0 || selected.y() > offset) selected.setY(offset);
state().selected = selected;
GtkTextIter startIter;
gtk_text_buffer_get_start_iter(gtkTextBuffer, &startIter);
gtk_text_iter_set_offset(&startIter, selected.x());
GtkTextIter endIter;
gtk_text_buffer_get_end_iter(gtkTextBuffer, &endIter);
gtk_text_iter_set_offset(&endIter, selected.y());
gtk_text_buffer_select_range(gtkTextBuffer, &startIter, &endIter);
unlock();
auto pSourceEdit::setNumbered(bool numbered) -> void {
gtk_source_view_set_show_line_numbers(gtkSourceView, numbered);
}
auto pSourceEdit::setScheme(const string& requestedScheme) -> void {
auto scheme = requestedScheme ? requestedScheme : "classic";
gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, scheme.downcase());
if(!gtkSourceStyleScheme) gtkSourceStyleScheme = gtk_source_style_scheme_manager_get_scheme(gtkSourceStyleSchemeManager, "classic");
gtk_source_buffer_set_style_scheme(gtkSourceBuffer, gtkSourceStyleScheme);
}
*/
auto pSourceEdit::setText(const string& text) -> void {
lock();
//prevent Ctrl+Z from undoing the newly assigned text ...
//for instance, a text editor widget setting the initial document here
gtk_source_buffer_begin_not_undoable_action(gtkSourceBuffer);
gtk_text_buffer_set_text(gtkTextBuffer, text, -1);
gtk_source_buffer_end_not_undoable_action(gtkSourceBuffer);
unlock();
}

View File

@@ -5,9 +5,13 @@ namespace hiro {
struct pSourceEdit : pWidget {
Declare(SourceEdit, Widget)
auto cursor() const -> Cursor;
auto setCursor(Cursor cursor) -> void;
auto setEditable(bool editable) -> void;
auto setFocused() -> void override;
auto setLanguage(const string& language) -> void;
auto setNumbered(bool numbered) -> void;
auto setScheme(const string& scheme) -> void;
auto setText(const string& text) -> void;
auto setWordWrap(bool wordWrap) -> void;
auto text() const -> string;

View File

@@ -294,30 +294,40 @@ auto pTableView::_doEdit(GtkCellRendererText* gtkCellRendererText, const char* p
}
}
auto pTableView::_doEvent(GdkEventButton* event) -> signed {
GtkTreePath* path = nullptr;
gtk_tree_view_get_path_at_pos(gtkTreeView, event->x, event->y, &path, nullptr, nullptr, nullptr);
auto pTableView::_doEvent(GdkEventButton* gdkEvent) -> signed {
if(gdkEvent->type == GDK_BUTTON_PRESS) {
//detect when the empty space of the GtkTreeView is clicked; and clear the selection
GtkTreePath* gtkPath = nullptr;
gtk_tree_view_get_path_at_pos(gtkTreeView, gdkEvent->x, gdkEvent->y, &gtkPath, nullptr, nullptr, nullptr);
if(!gtkPath) {
//the first time a GtkTreeView widget is clicked, even if the empty space of the widget is clicked,
//a "changed" signal will be sent after the "button-press-event", to activate the first item in the tree
//this is undesirable, so set a flag to undo the next selection change during the "changed" signal
suppressChange = true;
if(gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
gtk_tree_selection_unselect_all(gtkTreeSelection);
for(auto& item : state().items) item->setSelected(false);
self().doChange();
return true;
}
}
if(event->type == GDK_BUTTON_PRESS) {
//when clicking in empty space below the last table view item; GTK+ does not deselect all items;
//below code enables this functionality, to match behavior with all other UI toolkits (and because it's very convenient to have)
if(path == nullptr && gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
for(auto& item : state().items) item->setSelected(false);
self().doChange();
return true;
if(gdkEvent->button == 3) {
//multi-selection mode:
//if multiple items are selected, and one item is right-clicked on (for a context menu), GTK clears selection on all other items
//block this behavior so that onContext() handler can work on more than one selected item at a time
if(gtkPath && gtk_tree_selection_path_is_selected(gtkTreeSelection, gtkPath)) return true;
}
}
if(event->type == GDK_BUTTON_PRESS && event->button == 3) {
//this check prevents the loss of selection on other items if the item under the mouse cursor is currently selected
if(path && gtk_tree_selection_path_is_selected(gtkTreeSelection, path)) return true;
}
if(event->type == GDK_BUTTON_RELEASE && event->button == 3) {
//handle action during right-click release; as button-press-event is sent prior to selection update
//without this, the callback handler would see the previous selection state instead
self().doContext();
return false;
if(gdkEvent->type == GDK_BUTTON_RELEASE) {
suppressChange = false;
if(gdkEvent->button == 3) {
//handle action during right-click release; as button-press-event is sent prior to selection update
//without this, the callback handler would see the previous selection state instead
self().doContext();
return false;
}
}
return false;
@@ -365,6 +375,12 @@ auto pTableView::_doToggle(GtkCellRendererToggle* gtkCellRendererToggle, const c
//this prevents firing an onChange event when the actual selection has not changed
//this is particularly important for the motion-notify-event binding
auto pTableView::_updateSelected() -> void {
if(suppressChange) {
suppressChange = false;
gtk_tree_selection_unselect_all(gtkTreeSelection);
return;
}
vector<unsigned> selected;
GList* list = gtk_tree_selection_get_selected_rows(gtkTreeSelection, &gtkTreeModel);

View File

@@ -44,6 +44,7 @@ struct pTableView : pWidget {
GtkListStore* gtkListStore = nullptr;
GtkTreeModel* gtkTreeModel = nullptr;
vector<uint> currentSelection;
bool suppressChange = false;
};
}

View File

@@ -25,6 +25,8 @@ auto pTreeView::construct() -> void {
gtkTreeView = GTK_TREE_VIEW(gtkWidgetChild);
gtkTreeSelection = gtk_tree_view_get_selection(gtkTreeView);
gtk_tree_view_set_headers_visible(gtkTreeView, false);
gtk_tree_view_set_show_expanders(gtkTreeView, false);
gtk_tree_view_set_level_indentation(gtkTreeView, 20);
gtk_container_add(GTK_CONTAINER(gtkWidget), gtkWidgetChild);
gtk_widget_show(gtkWidgetChild);
@@ -58,10 +60,19 @@ auto pTreeView::construct() -> void {
g_signal_connect(G_OBJECT(gtkTreeSelection), "changed", G_CALLBACK(TreeView_change), (gpointer)this);
g_signal_connect(G_OBJECT(gtkCellToggle), "toggled", G_CALLBACK(TreeView_toggle), (gpointer)this);
//Ctrl+F triggers a small popup window at the bottom of the GtkTreeView, which clears the currently selected item(s)
//this is undesirable for amethyst, which uses the active item to display a document to edit, and binds Ctrl+F to a document find function
//for now, disable GtkTreeView's interactive search: longer term, more thought will need to go into if this is ever desirable or not
//gtk_tree_view_set_enable_search(gtkTreeView, false) does not work
//gtk_tree_view_set_search_column(gtkTreeView, -1) does not work
gtkEntry = (GtkEntry*)gtk_entry_new();
gtk_tree_view_set_search_entry(gtkTreeView, gtkEntry);
pWidget::construct();
}
auto pTreeView::destruct() -> void {
gtk_widget_destroy(GTK_WIDGET(gtkEntry));
gtk_widget_destroy(gtkWidgetChild);
gtk_widget_destroy(gtkWidget);
}
@@ -105,24 +116,39 @@ auto pTreeView::_activatePath(GtkTreePath* gtkPath) -> void {
}
auto pTreeView::_buttonEvent(GdkEventButton* gdkEvent) -> signed {
GtkTreePath* gtkPath = nullptr;
gtk_tree_view_get_path_at_pos(gtkTreeView, gdkEvent->x, gdkEvent->y, &gtkPath, nullptr, nullptr, nullptr);
if(gdkEvent->type == GDK_BUTTON_PRESS) {
//detect when the empty space of the GtkTreeView is clicked; and clear the selection
if(gtkPath == nullptr && gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
gtk_tree_selection_unselect_all(gtkTreeSelection);
state().selectedPath.reset();
self().doChange();
return true;
GtkTreePath* gtkPath = nullptr;
gtk_tree_view_get_path_at_pos(gtkTreeView, gdkEvent->x, gdkEvent->y, &gtkPath, nullptr, nullptr, nullptr);
if(!gtkPath) {
//the first time a GtkTreeView widget is clicked, even if the empty space of the widget is clicked,
//a "changed" signal will be sent after the "button-press-event", to activate the first item in the tree
//this is undesirable, so set a flag to undo the next selection change during the "changed" signal
suppressChange = true;
if(gtk_tree_selection_count_selected_rows(gtkTreeSelection) > 0) {
gtk_tree_selection_unselect_all(gtkTreeSelection);
state().selectedPath.reset();
self().doChange();
return true;
}
}
if(gdkEvent->button == 3) {
//multi-selection mode: (not implemented in TreeView yet ... but code is here anyway for future use)
//if multiple items are selected, and one item is right-clicked on (for a context menu), GTK clears selection on all other items
//block this behavior so that onContext() handler can work on more than one selected item at a time
if(gtkPath && gtk_tree_selection_path_is_selected(gtkTreeSelection, gtkPath)) return true;
}
}
if(gdkEvent->type == GDK_BUTTON_RELEASE && gdkEvent->button == 3) {
//handle right-click context menu
//have to detect on button release instead of press; as GTK+ does not update new selection prior to press event
self().doContext();
return false;
if(gdkEvent->type == GDK_BUTTON_RELEASE) {
suppressChange = false;
if(gdkEvent->button == 3) {
//handle action during right-click release; as button-press-event is sent prior to selection update
//without this, the callback handler would see the previous selection state instead
self().doContext();
return false;
}
}
return false;
@@ -173,6 +199,12 @@ auto pTreeView::_togglePath(string path) -> void {
}
auto pTreeView::_updateSelected() -> void {
if(suppressChange) {
suppressChange = false;
gtk_tree_selection_unselect_all(gtkTreeSelection);
return;
}
GtkTreeIter iter;
if(gtk_tree_selection_get_selected(gtkTreeSelection, &gtkTreeModel, &iter)) {
char* gtkPath = gtk_tree_model_get_string_from_iter(gtkTreeModel, &iter);

View File

@@ -27,6 +27,8 @@ struct pTreeView : pWidget {
GtkCellRenderer* gtkCellToggle = nullptr;
GtkCellRenderer* gtkCellPixbuf = nullptr;
GtkCellRenderer* gtkCellText = nullptr;
GtkEntry* gtkEntry = nullptr;
bool suppressChange = false;
};
}

View File

@@ -329,7 +329,9 @@ auto pWindow::setGeometry(Geometry geometry) -> void {
setMinimumSize(state().minimumSize);
auto time1 = chrono::millisecond();
while(chrono::millisecond() - time1 < 20) Application::processEvents();
while(chrono::millisecond() - time1 < 20) {
Application::processEvents();
}
gtk_window_resize(GTK_WINDOW(widget), geometry.width(), geometry.height() + _menuHeight() + _statusHeight());
@@ -627,7 +629,7 @@ auto pWindow::_synchronizeState() -> void {
if(!gtk_widget_get_realized(widget)) return;
#if defined(DISPLAY_WINDOWS)
auto window = GDK_WINDOW_HWND(gtk_widget_get_window(widget));
auto window = (HWND)GDK_WINDOW_HWND(gtk_widget_get_window(widget));
bool maximized = IsZoomed(window);
bool minimized = IsIconic(window);