diff options
Diffstat (limited to 'Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp')
| -rw-r--r-- | Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp | 539 |
1 files changed, 539 insertions, 0 deletions
diff --git a/Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp b/Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp new file mode 100644 index 0000000000..f531e8c1a0 --- /dev/null +++ b/Source/Core/DolphinWX/Input/InputConfigDiagBitmaps.cpp @@ -0,0 +1,539 @@ +// Copyright 2010 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include <algorithm> +#include <cmath> +#include <cstring> +#include <memory> +#include <string> +#include <vector> +#include <wx/bitmap.h> +#include <wx/brush.h> +#include <wx/colour.h> +#include <wx/dcmemory.h> +#include <wx/font.h> +#include <wx/graphics.h> +#include <wx/notebook.h> +#include <wx/pen.h> +#include <wx/settings.h> +#include <wx/statbmp.h> + +#include "DolphinWX/Input/InputConfigDiag.h" +#include "DolphinWX/WxUtils.h" + +#include "InputCommon/ControllerEmu.h" +#include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/ControllerInterface/Device.h" + +struct ShapePosition +{ + double max; + double diag; + double box; + double scale; + + double dz; + double range; + + wxCoord offset; +}; + +// regular octagon +static void DrawOctagon(wxGraphicsContext* gc, ShapePosition p) +{ + static constexpr int vertices = 8; + double radius = p.max; + + wxGraphicsPath path = gc->CreatePath(); + + double angle = 2.0 * M_PI / vertices; + + for (int i = 0; i < vertices; i++) + { + double a = angle * i; + double x = radius * cos(a); + double y = radius * sin(a); + if (i == 0) + path.MoveToPoint(x, y); + else + path.AddLineToPoint(x, y); + } + path.CloseSubpath(); + + wxGraphicsMatrix matrix = gc->CreateMatrix(); + matrix.Translate(p.offset, p.offset); + path.Transform(matrix); + + gc->DrawPath(path); +} + +// irregular dodecagon +static void DrawDodecagon(wxGraphicsContext* gc, ShapePosition p) +{ + wxGraphicsPath path = gc->CreatePath(); + path.MoveToPoint(p.dz, p.max); + path.AddLineToPoint(p.diag, p.diag); + path.AddLineToPoint(p.max, p.dz); + path.AddLineToPoint(p.max, -p.dz); + path.AddLineToPoint(p.diag, -p.diag); + path.AddLineToPoint(p.dz, -p.max); + path.AddLineToPoint(-p.dz, -p.max); + path.AddLineToPoint(-p.diag, -p.diag); + path.AddLineToPoint(-p.max, -p.dz); + path.AddLineToPoint(-p.max, p.dz); + path.AddLineToPoint(-p.diag, p.diag); + path.AddLineToPoint(-p.dz, p.max); + path.CloseSubpath(); + + wxGraphicsMatrix matrix = gc->CreateMatrix(); + matrix.Translate(p.offset, p.offset); + path.Transform(matrix); + + gc->DrawPath(path); +} + +static void DrawCenteredRectangle(wxGraphicsContext* gc, double x, double y, double w, double h) +{ + x -= w / 2; + y -= h / 2; + gc->DrawRectangle(x, y, w, h); +} + +#define VIS_BITMAP_SIZE 64 + +#define VIS_NORMALIZE(a) ((a / 2.0) + 0.5) +#define VIS_COORD(a) ((VIS_NORMALIZE(a)) * VIS_BITMAP_SIZE) + +#define COORD_VIS_SIZE 4 + +static void DrawCoordinate(wxGraphicsContext* gc, ControlState x, ControlState y) +{ + double xc = VIS_COORD(x); + double yc = VIS_COORD(y); + DrawCenteredRectangle(gc, xc, yc, COORD_VIS_SIZE, COORD_VIS_SIZE); +} + +static void DrawButton(const std::vector<unsigned int>& bitmasks, unsigned int buttons, + unsigned int n, wxGraphicsContext* gc, ControlGroupBox* g, unsigned int row, + const wxGraphicsMatrix& null_matrix) +{ + if (buttons & bitmasks[(row * 8) + n]) + { + gc->SetBrush(*wxRED_BRUSH); + } + else + { + unsigned char amt = 255 - g->control_group->controls[(row * 8) + n]->control_ref->State() * 128; + gc->SetBrush(wxBrush(wxColour(amt, amt, amt))); + } + gc->DrawRectangle(n * 12, (row == 0) ? 0 : (row * 11), 14, 12); + + // text + const std::string name = g->control_group->controls[(row * 8) + n]->name; + // Matrix transformation needs to be disabled so we don't draw scaled/zoomed text. + wxGraphicsMatrix old_matrix = gc->GetTransform(); + gc->SetTransform(null_matrix); + // bit of hax so ZL, ZR show up as L, R + gc->DrawText(wxUniChar((name[1] && name[1] < 'a') ? name[1] : name[0]), (n * 12 + 2) * g->m_scale, + (1 + (row == 0 ? 0 : row * 11)) * g->m_scale); + gc->SetTransform(old_matrix); +} + +static void DrawControlGroupBox(wxGraphicsContext* gc, ControlGroupBox* g) +{ + wxGraphicsMatrix null_matrix = gc->GetTransform(); + wxGraphicsMatrix scale_matrix = null_matrix; + scale_matrix.Scale(g->m_scale, g->m_scale); + + gc->SetTransform(scale_matrix); + + switch (g->control_group->type) + { + case GROUP_TYPE_TILT: + case GROUP_TYPE_STICK: + case GROUP_TYPE_CURSOR: + { + // this is starting to be a mess combining all these in one case + + ControlState x = 0, y = 0, z = 0; + + switch (g->control_group->type) + { + case GROUP_TYPE_STICK: + ((ControllerEmu::AnalogStick*)g->control_group)->GetState(&x, &y); + break; + case GROUP_TYPE_TILT: + ((ControllerEmu::Tilt*)g->control_group)->GetState(&x, &y); + break; + case GROUP_TYPE_CURSOR: + ((ControllerEmu::Cursor*)g->control_group)->GetState(&x, &y, &z); + break; + } + + // ir cursor forward movement + if (GROUP_TYPE_CURSOR == g->control_group->type) + { + gc->SetBrush(z ? *wxRED_BRUSH : *wxGREY_BRUSH); + wxGraphicsPath path = gc->CreatePath(); + path.AddRectangle(0, 31 - z * 31, 64, 2); + gc->FillPath(path); + } + + // input zone + gc->SetPen(*wxLIGHT_GREY_PEN); + if (GROUP_TYPE_STICK == g->control_group->type) + { + gc->SetBrush(wxColour(0xDDDDDD)); // Light Gray + + ShapePosition p; + p.box = 64; + p.offset = p.box / 2; + p.range = 256; + p.scale = p.box / p.range; + p.dz = 15 * p.scale; + bool octagon = false; + + if (g->control_group->name == "Main Stick") + { + p.max = 87 * p.scale; + p.diag = 55 * p.scale; + } + else if (g->control_group->name == "C-Stick") + { + p.max = 74 * p.scale; + p.diag = 46 * p.scale; + } + else + { + p.scale = 1; + p.max = 32; + octagon = true; + } + + if (octagon) + DrawOctagon(gc, p); + else + DrawDodecagon(gc, p); + } + else + { + gc->SetBrush(*wxWHITE_BRUSH); + gc->DrawRectangle(16, 16, 32, 32); + } + + if (GROUP_TYPE_CURSOR != g->control_group->type) + { + int deadzone_idx = g->control_group->type == GROUP_TYPE_STICK ? SETTING_DEADZONE : 0; + wxGraphicsPath path = gc->CreatePath(); + path.AddCircle(VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, + g->control_group->numeric_settings[deadzone_idx]->GetValue() * + VIS_BITMAP_SIZE / 2); + gc->SetBrush(*wxLIGHT_GREY_BRUSH); + gc->FillPath(path); + } + + // raw dot + ControlState xx, yy; + xx = g->control_group->controls[3]->control_ref->State(); + xx -= g->control_group->controls[2]->control_ref->State(); + yy = g->control_group->controls[1]->control_ref->State(); + yy -= g->control_group->controls[0]->control_ref->State(); + + gc->SetPen(*wxTRANSPARENT_PEN); + gc->SetBrush(*wxGREY_BRUSH); + DrawCoordinate(gc, xx, yy); + + // adjusted dot + if (x != 0 || y != 0) + { + gc->SetBrush(*wxRED_BRUSH); + // XXX: The adjusted values flip the Y axis to be in the format + // the Wii expects. Should this be in WiimoteEmu.cpp instead? + DrawCoordinate(gc, x, -y); + } + } + break; + + case GROUP_TYPE_FORCE: + { + ControlState raw_dot[3]; + ControlState adj_dot[3]; + const ControlState deadzone = g->control_group->numeric_settings[0]->GetValue(); + + // adjusted + ((ControllerEmu::Force*)g->control_group)->GetState(adj_dot); + + // raw + for (unsigned int i = 0; i < 3; ++i) + { + raw_dot[i] = (g->control_group->controls[i * 2 + 1]->control_ref->State() - + g->control_group->controls[i * 2]->control_ref->State()); + } + + // deadzone rect for forward/backward visual + gc->SetPen(*wxTRANSPARENT_PEN); + gc->SetBrush(*wxLIGHT_GREY_BRUSH); + int deadzone_height = deadzone * VIS_BITMAP_SIZE; + DrawCenteredRectangle(gc, 0, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE, deadzone_height); + +#define LINE_HEIGHT 2 + int line_y; + + // raw forward/background line + gc->SetBrush(*wxGREY_BRUSH); + line_y = VIS_COORD(raw_dot[2]); + DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT); + + // adjusted forward/background line + if (adj_dot[2] != 0.0) + { + gc->SetBrush(*wxRED_BRUSH); + line_y = VIS_COORD(adj_dot[2]); + DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, line_y, VIS_BITMAP_SIZE, LINE_HEIGHT); + } + +#define DEADZONE_RECT_SIZE 32 + + // empty deadzone square + gc->SetPen(*wxLIGHT_GREY_PEN); + gc->SetBrush(*wxWHITE_BRUSH); + DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, DEADZONE_RECT_SIZE, + DEADZONE_RECT_SIZE); + + // deadzone square + gc->SetPen(*wxTRANSPARENT_PEN); + gc->SetBrush(*wxLIGHT_GREY_BRUSH); + int dz_size = (deadzone * DEADZONE_RECT_SIZE); + DrawCenteredRectangle(gc, VIS_BITMAP_SIZE / 2, VIS_BITMAP_SIZE / 2, dz_size, dz_size); + + // raw dot + gc->SetBrush(*wxGREY_BRUSH); + DrawCoordinate(gc, raw_dot[1], raw_dot[0]); + + // adjusted dot + if (adj_dot[1] != 0 && adj_dot[0] != 0) + { + gc->SetBrush(*wxRED_BRUSH); + DrawCoordinate(gc, adj_dot[1], adj_dot[0]); + } + } + break; + + case GROUP_TYPE_BUTTONS: + { + const unsigned int button_count = static_cast<unsigned int>(g->control_group->controls.size()); + std::vector<unsigned int> bitmasks(button_count); + + // draw the shit + gc->SetPen(*wxGREY_PEN); + + for (unsigned int n = 0; n < button_count; ++n) + bitmasks[n] = (1 << n); + + unsigned int buttons = 0; + ((ControllerEmu::Buttons*)g->control_group)->GetState(&buttons, bitmasks.data()); + + // Draw buttons in rows of 8 + for (unsigned int row = 0; row < ceil((float)button_count / 8.0f); row++) + { + unsigned int buttons_to_draw = 8; + if ((button_count - row * 8) <= 8) + buttons_to_draw = button_count - row * 8; + + for (unsigned int n = 0; n < buttons_to_draw; ++n) + { + DrawButton(bitmasks, buttons, n, gc, g, row, null_matrix); + } + } + } + break; + + case GROUP_TYPE_TRIGGERS: + { + const unsigned int trigger_count = static_cast<unsigned int>(g->control_group->controls.size()); + std::vector<ControlState> trigs(trigger_count); + + // draw the shit + gc->SetPen(*wxGREY_PEN); + + ControlState deadzone = g->control_group->numeric_settings[0]->GetValue(); + ((ControllerEmu::Triggers*)g->control_group)->GetState(trigs.data()); + + for (unsigned int n = 0; n < trigger_count; ++n) + { + ControlState trig_r = g->control_group->controls[n]->control_ref->State(); + + // outline + gc->SetBrush(*wxWHITE_BRUSH); + gc->DrawRectangle(0, n * 12, 64, 14); + + // raw + gc->SetBrush(*wxGREY_BRUSH); + gc->DrawRectangle(0, n * 12, trig_r * 64, 14); + + // deadzone affected + gc->SetBrush(*wxRED_BRUSH); + gc->DrawRectangle(0, n * 12, trigs[n] * 64, 14); + + // text + // We don't want the text to be scaled/zoomed + gc->SetTransform(null_matrix); + gc->DrawText(StrToWxStr(g->control_group->controls[n]->name), 3 * g->m_scale, + (n * 12 + 1) * g->m_scale); + gc->SetTransform(scale_matrix); + } + + // deadzone box + gc->SetPen(*wxLIGHT_GREY_PEN); + gc->SetBrush(*wxTRANSPARENT_BRUSH); + gc->DrawRectangle(0, 0, deadzone * 64, trigger_count * 14); + } + break; + + case GROUP_TYPE_MIXED_TRIGGERS: + { + const unsigned int trigger_count = ((unsigned int)(g->control_group->controls.size() / 2)); + + // draw the shit + gc->SetPen(*wxGREY_PEN); + ControlState thresh = g->control_group->numeric_settings[0]->GetValue(); + + for (unsigned int n = 0; n < trigger_count; ++n) + { + gc->SetBrush(*wxRED_BRUSH); + + ControlState trig_d = g->control_group->controls[n]->control_ref->State(); + + ControlState trig_a = + trig_d > thresh ? 1 : g->control_group->controls[n + trigger_count]->control_ref->State(); + + gc->DrawRectangle(0, n * 12, 64 + 20, 14); + if (trig_d <= thresh) + gc->SetBrush(*wxWHITE_BRUSH); + gc->DrawRectangle(trig_a * 64, n * 12, 64 + 20, 14); + gc->DrawRectangle(64, n * 12, 32, 14); + + // text + // We don't want the text to be scaled/zoomed + gc->SetTransform(null_matrix); + gc->DrawText(StrToWxStr(g->control_group->controls[n + trigger_count]->name), 3 * g->m_scale, + (n * 12 + 1) * g->m_scale); + gc->DrawText(StrToWxStr(std::string(1, g->control_group->controls[n]->name[0])), + (64 + 3) * g->m_scale, (n * 12 + 1) * g->m_scale); + gc->SetTransform(scale_matrix); + } + + // threshold box + gc->SetPen(*wxLIGHT_GREY_PEN); + gc->SetBrush(*wxTRANSPARENT_BRUSH); + gc->DrawRectangle(thresh * 64, 0, 128, trigger_count * 14); + } + break; + + case GROUP_TYPE_SLIDER: + { + const ControlState deadzone = g->control_group->numeric_settings[0]->GetValue(); + + ControlState state = g->control_group->controls[1]->control_ref->State() - + g->control_group->controls[0]->control_ref->State(); + gc->SetPen(*wxTRANSPARENT_PEN); + gc->SetBrush(*wxGREY_BRUSH); + gc->DrawRectangle(31 + state * 30, 0, 2, 14); + + ControlState adj_state; + ((ControllerEmu::Slider*)g->control_group)->GetState(&adj_state); + if (state) + { + gc->SetBrush(*wxRED_BRUSH); + gc->DrawRectangle(31 + adj_state * 30, 0, 2, 14); + } + + // deadzone box + gc->SetPen(*wxLIGHT_GREY_PEN); + gc->SetBrush(*wxTRANSPARENT_BRUSH); + gc->DrawRectangle(32 - deadzone * 32, 0, deadzone * 64, 14); + } + break; + + default: + break; + } + gc->SetTransform(null_matrix); +} + +static void DrawBorder(wxGraphicsContext* gc, double scale) +{ + double pen_width = std::round(scale); // Pen width = 1px * scale + + // Use the window caption bar color as a safe accent color. + wxPen border_pen(wxSystemSettings::GetColour(wxSYS_COLOUR_ACTIVECAPTION), + static_cast<int>(pen_width)); + border_pen.SetCap(wxCAP_PROJECTING); + + double width, height; + gc->GetSize(&width, &height); + + wxGraphicsPath path = gc->CreatePath(); + path.AddRectangle(pen_width / 2, pen_width / 2, width - pen_width, height - pen_width); + gc->SetPen(border_pen); + gc->StrokePath(path); +} + +void InputConfigDialog::UpdateBitmaps(wxTimerEvent& WXUNUSED(event)) +{ + wxFont small_font(6, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD); + const wxColour font_color{0xB8B8B8}; + + g_controller_interface.UpdateInput(); + + wxMemoryDC dc; + auto lock = ControllerEmu::GetStateLock(); + for (ControlGroupBox* g : control_groups) + { + // Only if this control group has a bitmap + if (!g->static_bitmap) + continue; + + wxBitmap bitmap(g->static_bitmap->GetBitmap()); + // NOTE: Selecting the bitmap inherits the bitmap's ScaleFactor onto the DC as well. + dc.SelectObjectAsSource(bitmap); + dc.SetBackground(*wxWHITE_BRUSH); + dc.Clear(); + +#ifdef __WXGTK20__ + int dc_height = 0; + dc.SetFont(small_font); + dc.GetTextExtent(g->control_group->name, nullptr, &dc_height); +#endif + + std::unique_ptr<wxGraphicsContext> gc{wxGraphicsContext::Create(dc)}; + gc->DisableOffset(); + gc->SetFont(small_font, font_color); + +#ifdef __WXGTK20__ + double gc_height = 0; + gc->GetTextExtent(g->control_group->name, nullptr, &gc_height); + // On GTK2, wx creates a new empty Cairo/Pango context for the graphics context instead + // of reusing the wxMemoryDC one, this causes it to forget the screen DPI so fonts stop + // scaling, we need to scale it manually instead. + if (std::ceil(gc_height) < dc_height) + { + wxFont fixed_font(small_font); + fixed_font.SetPointSize(static_cast<int>(fixed_font.GetPointSize() * g->m_scale)); + gc->SetFont(fixed_font, font_color); + } +#endif + + DrawControlGroupBox(gc.get(), g); + DrawBorder(gc.get(), g->m_scale); + + // label for sticks and stuff + if (g->HasBitmapHeading()) + gc->DrawText(StrToWxStr(g->control_group->name).Upper(), 4 * g->m_scale, 2 * g->m_scale); + + gc.reset(); + dc.SelectObject(wxNullBitmap); + g->static_bitmap->SetBitmap(bitmap); + } +} |
