From b9b3b0288237c2337cff87f48de94eb1274ed004 Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 1 Nov 2014 11:36:51 -0300 Subject: [PATCH 1/6] Fix mouse issues on OS X (fix #458) Now we generate she::Event::MouseEnter/Leave events correctly when the mouse leaves or enters into the window to show the native cursor again when necessary. Close #466 --- .../include/allegro/platform/aintosx.h | 8 +++-- src/allegro/src/macosx/qzmouse.m | 13 +++++--- src/allegro/src/macosx/system.m | 8 +++++ src/she/alleg4/she_alleg4.cpp | 33 ++++++++++++++++++- src/ui/cursor_type.h | 2 ++ src/ui/manager.cpp | 16 +++++---- src/ui/system.cpp | 11 +++++-- 7 files changed, 73 insertions(+), 18 deletions(-) diff --git a/src/allegro/include/allegro/platform/aintosx.h b/src/allegro/include/allegro/platform/aintosx.h index 492cc2896..8f4191082 100644 --- a/src/allegro/include/allegro/platform/aintosx.h +++ b/src/allegro/include/allegro/platform/aintosx.h @@ -181,12 +181,14 @@ AL_VAR(int, osx_mouse_warped); AL_VAR(int, osx_skip_mouse_move); AL_VAR(int, osx_emulate_mouse_buttons); AL_VAR(NSTrackingRectTag, osx_mouse_tracking_rect); -extern AL_METHOD(void, osx_window_close_hook, (void)); -extern AL_METHOD(void, osx_resize_callback, (RESIZE_DISPLAY_EVENT *ev)); - #endif +extern AL_METHOD(void, osx_window_close_hook, (void)); +extern AL_METHOD(void, osx_resize_callback, (RESIZE_DISPLAY_EVENT *ev)); +extern AL_METHOD(void, osx_mouse_enter_callback, (void)); +extern AL_METHOD(void, osx_mouse_leave_callback, (void)); + #endif /* Local variables: */ diff --git a/src/allegro/src/macosx/qzmouse.m b/src/allegro/src/macosx/qzmouse.m index 2db42089e..cb6fd1438 100644 --- a/src/allegro/src/macosx/qzmouse.m +++ b/src/allegro/src/macosx/qzmouse.m @@ -255,7 +255,12 @@ static void osx_mouse_set_range(int x1, int y1, int x2, int y2) mouse_maxx = x2; mouse_maxy = y2; - osx_mouse_position(CLAMP(mouse_minx, _mouse_x, mouse_maxx), CLAMP(mouse_miny, _mouse_y, mouse_maxy)); + // Do not change the position of the mouse inside the + // range to avoid changing the position to 0,0 when + // the program starts. + //osx_mouse_position( + // CLAMP(mouse_minx, _mouse_x, mouse_maxx), + // CLAMP(mouse_miny, _mouse_y, mouse_maxy)); } @@ -418,13 +423,13 @@ static int osx_select_system_cursor(AL_CONST int cursor) requested_cursor = [NSCursor operationNotAllowedCursor]; break; case MOUSE_CURSOR_SIZE_N: - requested_cursor = [NSCursor resizeUpCursor]; break; + requested_cursor = [NSCursor resizeUpCursor]; break; case MOUSE_CURSOR_SIZE_S: - requested_cursor = [NSCursor resizeDownCursor]; break; + requested_cursor = [NSCursor resizeDownCursor]; break; case MOUSE_CURSOR_SIZE_NS: - requested_cursor = [NSCursor resizeUpDownCursor]; break; + requested_cursor = [NSCursor resizeUpDownCursor]; break; case MOUSE_CURSOR_SIZE_W: requested_cursor = [NSCursor resizeLeftCursor]; diff --git a/src/allegro/src/macosx/system.m b/src/allegro/src/macosx/system.m index 7e890f895..113cf5c15 100644 --- a/src/allegro/src/macosx/system.m +++ b/src/allegro/src/macosx/system.m @@ -69,6 +69,8 @@ AllegroWindow *osx_window = NULL; char osx_window_title[ALLEGRO_MESSAGE_SIZE]; void (*osx_window_close_hook)(void) = NULL; void (*osx_resize_callback)(RESIZE_DISPLAY_EVENT *ev) = NULL; +void (*osx_mouse_enter_callback)() = NULL; +void (*osx_mouse_leave_callback)() = NULL; int osx_gfx_mode = OSX_GFX_NONE; int osx_emulate_mouse_buttons = FALSE; int osx_window_first_expose = FALSE; @@ -295,6 +297,9 @@ void osx_event_handler() _mouse_on = TRUE; osx_hide_native_mouse(); } + + if (osx_mouse_enter_callback) + osx_mouse_enter_callback(); } } [NSApp sendEvent: event]; @@ -308,6 +313,9 @@ void osx_event_handler() _mouse_on = FALSE; osx_show_native_mouse(); } + + if (osx_mouse_leave_callback) + osx_mouse_leave_callback(); } } [NSApp sendEvent: event]; diff --git a/src/she/alleg4/she_alleg4.cpp b/src/she/alleg4/she_alleg4.cpp index 7f11b6d29..1610f92b7 100644 --- a/src/she/alleg4/she_alleg4.cpp +++ b/src/she/alleg4/she_alleg4.cpp @@ -35,7 +35,9 @@ #ifndef WM_MOUSEHWHEEL #define WM_MOUSEHWHEEL 0x020E #endif + #elif defined(ALLEGRO_UNIX) + #include #ifdef None #undef None @@ -50,6 +52,10 @@ #include "she/clipboard_simple.h" #endif +#ifdef __APPLE__ + #include +#endif + #include "loadpng.h" #include @@ -407,7 +413,27 @@ void unsubclass_hwnd(HWND hwnd) base_wndproc = NULL; } -#endif +#endif // WIN32 + +#if __APPLE__ + +void osx_mouser_enter_she_callback() +{ + Event ev; + ev.setPosition(gfx::Point(0, 0)); + ev.setType(Event::MouseEnter); + queue_event(ev); +} + +void osx_mouser_leave_she_callback() +{ + Event ev; + ev.setType(Event::MouseLeave); + queue_event(ev); +} + +#endif // __APPLE__ + } // anonymous namespace class Alleg4Display : public Display { @@ -417,6 +443,11 @@ public: , m_scale(0) { unique_display = this; +#ifdef __APPLE__ + osx_mouse_enter_callback = osx_mouser_enter_she_callback; + osx_mouse_leave_callback = osx_mouser_leave_she_callback; +#endif + if (install_mouse() < 0) throw DisplayCreationException(allegro_error); if (install_keyboard() < 0) throw DisplayCreationException(allegro_error); diff --git a/src/ui/cursor_type.h b/src/ui/cursor_type.h index ed8b74671..e1625ad9e 100644 --- a/src/ui/cursor_type.h +++ b/src/ui/cursor_type.h @@ -11,6 +11,8 @@ namespace ui { enum CursorType { + kOutsideDisplay = -1, + kFirstCursorType = 0, kNoCursor = 0, kArrowCursor, diff --git a/src/ui/manager.cpp b/src/ui/manager.cpp index 49d5b9eb6..ad13712a3 100644 --- a/src/ui/manager.cpp +++ b/src/ui/manager.cpp @@ -102,6 +102,9 @@ static char old_readed_key[KEY_MAX]; /* keyboard status of previous static unsigned key_repeated[KEY_MAX]; +// Flag to block all the generation of mouse messages from polling. +static bool mouse_left = false; + /* keyboard focus movement stuff */ static bool move_focus(Manager* manager, Message* msg); static int count_widgets_accept_focus(Widget* widget); @@ -288,6 +291,9 @@ bool Manager::generateMessages() void Manager::generateMouseMessages() { + if (mouse_left) + return; + // Update mouse status bool mousemove = _internal_poll_mouse(); @@ -469,18 +475,14 @@ void Manager::generateMessagesFromSheEvents() } case she::Event::MouseEnter: { - if (!mouse_events_from_she) - continue; - + mouse_left = false; jmouse_set_cursor(kArrowCursor); break; } case she::Event::MouseLeave: { - if (!mouse_events_from_she) - continue; - - jmouse_set_cursor(kNoCursor); + mouse_left = true; + jmouse_set_cursor(kOutsideDisplay); setMouse(NULL); _internal_no_mouse_position(); diff --git a/src/ui/system.cpp b/src/ui/system.cpp index cfea56edf..6b5648af2 100644 --- a/src/ui/system.cpp +++ b/src/ui/system.cpp @@ -98,11 +98,14 @@ static void update_mouse_cursor() // Use native cursor when it's possible/available/configured to do so. bool native_cursor_available = false; - if (use_native_mouse_cursor) { + if (use_native_mouse_cursor || mouse_cursor_type == kOutsideDisplay) { she::NativeCursor nativeCursor = she::kNoCursor; native_cursor_available = true; switch (mouse_cursor_type) { + case ui::kOutsideDisplay: + nativeCursor = she::kArrowCursor; + break; case ui::kNoCursor: break; case ui::kArrowCursor: case ui::kArrowPlusCursor: @@ -151,10 +154,12 @@ static void update_mouse_cursor() // Use a software cursor with the overlay. if (!native_cursor_set) { - if (mouse_cursor_type == ui::kNoCursor) + if (mouse_cursor_type == ui::kNoCursor) { update_mouse_overlay(NULL); - else + } + else { update_mouse_overlay(CurrentTheme::get()->getCursor(mouse_cursor_type)); + } } else { // Hide the overlay if we are using a native cursor. From 22c3651c635a4bf93f81bd8dd1b2e7c0982a0bfc Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 1 Nov 2014 12:44:07 -0300 Subject: [PATCH 2/6] Fix Alt+Mnemonic on Mac OS X --- src/ui/button.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ui/button.cpp b/src/ui/button.cpp index 70bd09d9a..e975edab5 100644 --- a/src/ui/button.cpp +++ b/src/ui/button.cpp @@ -105,7 +105,7 @@ bool ButtonBase::onProcessMessage(Message* msg) bool mnemonicPressed = (msg->altPressed() && getMnemonicChar() && - getMnemonicChar() == tolower(keymsg->unicodeChar())); + getMnemonicChar() == scancode_to_unicode(keymsg->scancode())); // For kButtonWidget if (m_behaviorType == kButtonWidget) { From 525fbd0d46f6b5b587332644d105416c5146ea1b Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 1 Nov 2014 13:32:02 -0300 Subject: [PATCH 3/6] Don't add extra ASEPRITE block to .gif file because Photoshop cannot load them --- src/app/file/gif_format.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/app/file/gif_format.cpp b/src/app/file/gif_format.cpp index d92794348..7dc0c4560 100644 --- a/src/app/file/gif_format.cpp +++ b/src/app/file/gif_format.cpp @@ -707,20 +707,6 @@ bool GifFormat::onSave(FileOp* fop) throw Exception("Error writing GIF graphics extension record (trailer section)."); } - // Add Aseprite block (at this moment, it's empty). - if (frame_num == 0) { - if (EGifPutExtensionLeader(gif_file, APPLICATION_EXT_FUNC_CODE) == GIF_ERROR) - throw Exception("Error writing GIF comment (header section)."); - - unsigned char extension_bytes[11]; - memcpy(extension_bytes, "ASEPRITE1.0", 11); - if (EGifPutExtensionBlock(gif_file, sizeof(extension_bytes), extension_bytes) == GIF_ERROR) - throw Exception("Error writing GIF comment (first block)."); - - if (EGifPutExtensionTrailer(gif_file) == GIF_ERROR) - throw Exception("Error writing GIF comment (trailer section)."); - } - // Write graphics extension record (to save the duration of the // frame and maybe the transparency index). { From 8ac9ebb64dfa67d1e17d7fa0ebdf0970419a1f4c Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 1 Nov 2014 15:38:26 -0300 Subject: [PATCH 4/6] Fix bug moving cels between layers The image wasn't being copied correctly when the source cel's (x,y) was != (0,0) --- src/app/document_api.cpp | 7 +--- src/app/document_api_tests.cpp | 72 ++++++++++++++++++++++++++++++++++ src/app/test_context.h | 2 + 3 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 src/app/document_api_tests.cpp diff --git a/src/app/document_api.cpp b/src/app/document_api.cpp index 183ae49cb..895c3e0a0 100644 --- a/src/app/document_api.cpp +++ b/src/app/document_api.cpp @@ -740,13 +740,10 @@ void DocumentApi::moveCel( // Move the cel between different layers. else { if (!dstCel) { + dstImage = Image::createCopy(srcImage); + dstCel = new Cel(*srcCel); dstCel->setFrame(dstFrame); - dstImage = crop_image(srcImage, - -srcCel->x(), - -srcCel->y(), - dstSprite->width(), // TODO dstSprite or srcSprite - dstSprite->height(), 0); dstCel->setImage(addImageInStock(dstSprite, dstImage)); } diff --git a/src/app/document_api_tests.cpp b/src/app/document_api_tests.cpp new file mode 100644 index 000000000..7942ff310 --- /dev/null +++ b/src/app/document_api_tests.cpp @@ -0,0 +1,72 @@ +/* Aseprite + * Copyright (C) 2001-2014 David Capello + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "tests/test.h" + +#include "app/document_api.h" +#include "app/test_context.h" +#include "base/unique_ptr.h" +#include "raster/cel.h" +#include "raster/image.h" +#include "raster/primitives.h" + +using namespace app; +using namespace raster; + +typedef base::UniquePtr DocumentPtr; + +TEST(DocumentApi, MoveCel) { + TestContext ctx; + DocumentPtr doc(static_cast(ctx.documents().add(32, 16))); + Sprite* sprite = doc->sprite(); + LayerImage* layer1 = dynamic_cast(sprite->folder()->getFirstLayer()); + LayerImage* layer2 = new LayerImage(sprite); + + Cel* cel1 = layer1->getCel(FrameNumber(0)); + cel1->setPosition(2, -2); + cel1->setOpacity(128); + + Image* image1 = cel1->image(); + EXPECT_EQ(32, image1->width()); + EXPECT_EQ(16, image1->height()); + for (int v=0; vheight(); ++v) + for (int u=0; uwidth(); ++u) + image1->putPixel(u, v, u+v*image1->width()); + + // Create a copy for later comparison. + base::UniquePtr expectedImage(Image::createCopy(image1)); + + doc->getApi().moveCel( + layer1, FrameNumber(0), + layer2, FrameNumber(1)); + + EXPECT_EQ(NULL, layer1->getCel(FrameNumber(0))); + + Cel* cel2 = layer2->getCel(FrameNumber(1)); + ASSERT_TRUE(cel2 != NULL); + + Image* image2 = cel2->image(); + EXPECT_EQ(32, image2->width()); + EXPECT_EQ(16, image2->height()); + EXPECT_EQ(0, count_diff_between_images(expectedImage, image2)); + EXPECT_EQ(2, cel2->x()); + EXPECT_EQ(-2, cel2->y()); + EXPECT_EQ(128, cel2->opacity()); + + doc->close(); +} diff --git a/src/app/test_context.h b/src/app/test_context.h index 471b40d44..6427fd85c 100644 --- a/src/app/test_context.h +++ b/src/app/test_context.h @@ -21,9 +21,11 @@ #pragma once #include "app/context.h" +#include "app/document.h" #include "app/document_location.h" #include "doc/settings.h" #include "raster/layer.h" +#include "raster/sprite.h" namespace app { From 58b89ded4ad751f8cbb96591e691404ae3cf449a Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 1 Nov 2014 15:39:00 -0300 Subject: [PATCH 5/6] Add some DocRangeOps.MoveCels tests --- src/app/document_range_tests.cpp | 77 +++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/src/app/document_range_tests.cpp b/src/app/document_range_tests.cpp index 83931a247..2c88f3d93 100644 --- a/src/app/document_range_tests.cpp +++ b/src/app/document_range_tests.cpp @@ -78,6 +78,12 @@ typedef base::UniquePtr DocumentPtr; EXPECT_TRUE(expect_frame(f, 5)); \ EXPECT_TRUE(expect_frame(g, 6)); +#define EXPECT_CEL(y, x, v, u) \ + EXPECT_TRUE(expect_cel(y, x, v, u)); + +#define EXPECT_EMPTY_CEL(y, x) \ + EXPECT_TRUE(expect_empty_cel(y, x)); + class DocRangeOps : public ::testing::Test { public: DocRangeOps() { @@ -147,19 +153,11 @@ protected: bool expect_layer_frame(int expected_layer, int expected_frame, int layer, int frame) { if (frame >= 0) { - color_t expected_color = white; + if (!expect_cel(expected_layer, expected_frame, layer, frame)) + return false; - color_t color = get_pixel( - static_cast(sprite->indexToLayer(LayerIndex(layer))) - ->getCel(FrameNumber(frame))->image(), - expected_layer, expected_frame); - - EXPECT_EQ(expected_color, color); EXPECT_EQ((expected_frame+1), sprite->getFrameDuration(FrameNumber(frame))); - - return - (expected_color == color - && (expected_frame+1) == sprite->getFrameDuration(FrameNumber(frame))); + return ((expected_frame+1) == sprite->getFrameDuration(FrameNumber(frame))); } if (layer >= 0) { @@ -173,6 +171,27 @@ protected: return true; } + bool expect_cel(int expected_layer, int expected_frame, int layer, int frame) { + color_t expected_color = white; + + color_t color = get_pixel( + static_cast(sprite->indexToLayer(LayerIndex(layer))) + ->getCel(FrameNumber(frame))->image(), + expected_layer, expected_frame); + + EXPECT_EQ(expected_color, color); + + return (expected_color == color); + } + + bool expect_empty_cel(int layer, int frame) { + Cel* cel = static_cast(sprite->indexToLayer(LayerIndex(layer))) + ->getCel(FrameNumber(frame)); + + EXPECT_EQ(NULL, cel); + return (cel == NULL); + } + TestContext ctx; DocumentPtr doc; Sprite* sprite; @@ -222,6 +241,10 @@ inline DocumentRange frames_range(int frame) { return range(0, frame, 0, frame, DocumentRange::kFrames); } +inline DocumentRange cels_range(int fromLayer, int fromFrNum, int toLayer, int toFrNum) { + return range(fromLayer, fromFrNum, toLayer, toFrNum, DocumentRange::kCels); +} + TEST_F(DocRangeOps, MoveLayersNoOp) { // Move one layer to the same place @@ -679,7 +702,37 @@ TEST_F(DocRangeOps, MoveFrames) { } TEST_F(DocRangeOps, MoveCels) { - // TODO + DocumentRangePlace ignore = kDocumentRangeBefore; + + move_range(doc, + cels_range(0, 0, 0, 0), + cels_range(0, 1, 0, 1), ignore); + EXPECT_CEL(0, 0, 0, 1); + EXPECT_EMPTY_CEL(0, 0); + doc->getUndo()->doUndo(); + EXPECT_CEL(0, 0, 0, 0); + + move_range(doc, + cels_range(0, 0, 0, 1), + cels_range(0, 2, 0, 3), ignore); + EXPECT_CEL(0, 0, 0, 2); + EXPECT_CEL(0, 1, 0, 3); + EXPECT_EMPTY_CEL(0, 0); + EXPECT_EMPTY_CEL(0, 1); + doc->getUndo()->doUndo(); + + move_range(doc, + cels_range(0, 0, 0, 3), + cels_range(1, 0, 1, 3), ignore); + EXPECT_CEL(0, 0, 1, 0); + EXPECT_CEL(0, 1, 1, 1); + EXPECT_CEL(0, 2, 1, 2); + EXPECT_CEL(0, 3, 1, 3); + EXPECT_EMPTY_CEL(0, 0); + EXPECT_EMPTY_CEL(0, 1); + EXPECT_EMPTY_CEL(0, 2); + EXPECT_EMPTY_CEL(0, 3); + doc->getUndo()->doUndo(); } TEST_F(DocRangeOps, CopyLayers) { From ef25706ed2e1412eb25e5cf108b0c1ffd93fe5bd Mon Sep 17 00:00:00 2001 From: David Capello Date: Sat, 1 Nov 2014 15:46:13 -0300 Subject: [PATCH 6/6] Fix crash in SelectAccelerator::KeyField when a Unicode char is pressed The problem is that Accelerator::toString() wasn't returning valid utf-8 strings for Unicode characters. --- src/ui/accelerator.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ui/accelerator.cpp b/src/ui/accelerator.cpp index 4892d5f3c..b9d5179d1 100644 --- a/src/ui/accelerator.cpp +++ b/src/ui/accelerator.cpp @@ -322,8 +322,11 @@ std::string Accelerator::toString() const if (m_modifiers & kKeySpaceModifier) buf += "Space+"; // Key - if (m_unicodeChar) - buf += (wchar_t)toupper(m_unicodeChar); + if (m_unicodeChar) { + std::wstring wideUnicodeChar; + wideUnicodeChar.push_back((wchar_t)toupper(m_unicodeChar)); + buf += base::to_utf8(wideUnicodeChar); + } else if (m_scancode && m_scancode > 0 && m_scancode < (int)table_size) buf += table[m_scancode]; else if (!buf.empty() && buf[buf.size()-1] == '+')