GTK: hybrid layout support (#773)

* GTK: hybrid layout support
* Fix HUD with new layouts
* Simplify rotoscaled_hudedit logic a bit
This commit is contained in:
Max Fedotov 2024-02-26 22:12:50 +03:00 committed by GitHub
parent 45738beb88
commit 0a6eca6dce
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 261 additions and 41 deletions

View File

@ -313,6 +313,8 @@ enum orientation_enum {
ORIENT_VERTICAL = 0,
ORIENT_HORIZONTAL = 1,
ORIENT_SINGLE = 2,
ORIENT_HYBRID_EQUAL = 3,
ORIENT_HYBRID_VERTICAL = 4,
ORIENT_N
};
@ -322,9 +324,11 @@ struct screen_size_t {
};
const struct screen_size_t screen_size[ORIENT_N] = {
{256, 384},
{512, 192},
{256, 192}
{GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT*2},
{GPU_FRAMEBUFFER_NATIVE_WIDTH*2, GPU_FRAMEBUFFER_NATIVE_HEIGHT},
{GPU_FRAMEBUFFER_NATIVE_WIDTH, GPU_FRAMEBUFFER_NATIVE_HEIGHT},
{GPU_FRAMEBUFFER_NATIVE_WIDTH*3, GPU_FRAMEBUFFER_NATIVE_HEIGHT*2},
{GPU_FRAMEBUFFER_NATIVE_WIDTH*3, GPU_FRAMEBUFFER_NATIVE_HEIGHT*2}
};
enum spumode_enum {
@ -539,7 +543,9 @@ struct nds_screen_t {
gint rotation_angle;
orientation_enum orientation;
cairo_matrix_t touch_matrix;
cairo_matrix_t touch_matrix_hybrid;
cairo_matrix_t topscreen_matrix;
cairo_matrix_t topscreen_matrix_hybrid;
gboolean swap;
};
@ -1213,6 +1219,33 @@ static void Reset(GSimpleAction *action, GVariant *parameter, gpointer user_data
/////////////////////////////// DRAWING SCREEN //////////////////////////////////
struct DrawParams {
bool valid;
//Drawing area size
gint daW;
gint daH;
//Image size
gint imgW;
gint imgH;
//Buffer size
gint dstW;
gint dstH;
gint dstScale;
gint gap;
gint gapOffset;
gfloat ratio;
gdouble hybridBigScreenScale;
};
static DrawParams draw_params = {false, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.0, 0.0};
static void UpdateDrawingAreaAspect()
{
gint H, W;
@ -1226,11 +1259,16 @@ static void UpdateDrawingAreaAspect()
}
if (nds_screen.orientation != ORIENT_SINGLE) {
if (nds_screen.orientation == ORIENT_VERTICAL) {
if ((nds_screen.orientation == ORIENT_VERTICAL) || (nds_screen.orientation == ORIENT_HYBRID_EQUAL) ||
(nds_screen.orientation == ORIENT_HYBRID_VERTICAL)) {
if ((nds_screen.rotation_angle == 0 || nds_screen.rotation_angle == 180)) {
H += nds_screen.gap_size;
if((nds_screen.orientation == ORIENT_HYBRID_EQUAL) || (nds_screen.orientation == ORIENT_HYBRID_VERTICAL))
W = (W * 2.0/3.0) * (double)(H + nds_screen.gap_size) / (double)H + W / 3.0;
H += nds_screen.gap_size;
} else {
W += nds_screen.gap_size;
if((nds_screen.orientation == ORIENT_HYBRID_EQUAL) || (nds_screen.orientation == ORIENT_HYBRID_VERTICAL))
H = (H * 2.0/3.0) * (double)(W + nds_screen.gap_size) / (double)W + H / 3.0;
W += nds_screen.gap_size;
}
}
}
@ -1243,6 +1281,8 @@ static void UpdateDrawingAreaAspect()
gtk_window_set_resizable(GTK_WINDOW(pWindow), FALSE);
gtk_widget_set_size_request(GTK_WIDGET(pDrawingArea), W * winsize_current / 2, H * winsize_current / 2);
}
draw_params.valid = false;
}
static void ToggleGap(GSimpleAction *action, GVariant *parameter, gpointer user_data)
@ -1306,9 +1346,15 @@ static void SetOrientation(GSimpleAction *action, GVariant *parameter, gpointer
orient = ORIENT_HORIZONTAL;
else if(strcmp(string, "single") == 0)
orient = ORIENT_SINGLE;
else if(strcmp(string, "hybrid_equal") == 0)
orient = ORIENT_HYBRID_EQUAL;
else if(strcmp(string, "hybrid_vertical") == 0)
orient = ORIENT_HYBRID_VERTICAL;
nds_screen.orientation = orient;
#ifdef HAVE_LIBAGG
osd->singleScreen = nds_screen.orientation == ORIENT_SINGLE;
bool hybrid = (nds_screen.orientation == ORIENT_HYBRID_EQUAL) || (nds_screen.orientation == ORIENT_HYBRID_VERTICAL);
osd->swapScreens = nds_screen.swap && !hybrid;
#endif
config.view_orient = nds_screen.orientation;
UpdateDrawingAreaAspect();
@ -1320,7 +1366,8 @@ static void ToggleSwapScreens(GSimpleAction *action, GVariant *parameter, gpoint
gboolean value = !g_variant_get_boolean(variant);
nds_screen.swap = value;
#ifdef HAVE_LIBAGG
osd->swapScreens = nds_screen.swap;
bool hybrid = (nds_screen.orientation == ORIENT_HYBRID_EQUAL) || (nds_screen.orientation == ORIENT_HYBRID_VERTICAL);
osd->swapScreens = nds_screen.swap && !hybrid;
#endif
config.view_swap = nds_screen.swap;
RedrawScreen();
@ -1364,6 +1411,17 @@ static inline void drawTopScreen(cairo_t* cr, u32* buf, gint w, gint h, gint gap
cairo_translate(cr, w, 0);
}
break;
case ORIENT_HYBRID_EQUAL:
case ORIENT_HYBRID_VERTICAL:
if (!swap && (draw_params.hybridBigScreenScale > 0.0)) {
cairo_save(cr);
cairo_scale(cr, draw_params.hybridBigScreenScale, draw_params.hybridBigScreenScale);
cairo_get_matrix(cr, &nds_screen.topscreen_matrix_hybrid);
drawScreen(cr, buf, w, h);
cairo_restore(cr);
}
cairo_translate(cr, w * draw_params.hybridBigScreenScale, 0);
break;
}
// Used for HUD editor mode
cairo_get_matrix(cr, &nds_screen.topscreen_matrix);
@ -1387,6 +1445,19 @@ static inline void drawBottomScreen(cairo_t* cr, u32* buf, gint w, gint h, gint
cairo_translate(cr, w, 0);
}
break;
case ORIENT_HYBRID_EQUAL:
case ORIENT_HYBRID_VERTICAL:
if (swap && (draw_params.hybridBigScreenScale > 0.0)) {
cairo_save(cr);
if(orientation == ORIENT_HYBRID_VERTICAL)
cairo_translate(cr, 0, h * (2.0 - draw_params.hybridBigScreenScale) + gap);
cairo_scale(cr, draw_params.hybridBigScreenScale, draw_params.hybridBigScreenScale);
cairo_get_matrix(cr, &nds_screen.touch_matrix_hybrid);
drawScreen(cr, buf, w, h);
cairo_restore(cr);
}
cairo_translate(cr, w * draw_params.hybridBigScreenScale, h + gap);
break;
}
// Store the matrix for converting touchscreen coordinates
cairo_get_matrix(cr, &nds_screen.touch_matrix);
@ -1394,6 +1465,21 @@ static inline void drawBottomScreen(cairo_t* cr, u32* buf, gint w, gint h, gint
cairo_restore(cr);
}
static void calc_hybrid_vertical_params(gint daW, gint daH, gint gap, gint dstScale, gint & imgW, gint & imgH)
{
gdouble hyb_hratio1, hyb_hratio2, hyb_vratio1, hyb_vratio2, hyb_ratio1, hyb_ratio2;
hyb_hratio1 = (gdouble)daW / (gdouble)(GPU_FRAMEBUFFER_NATIVE_WIDTH * dstScale / 2);
hyb_vratio1 = (gdouble)daH / (gdouble)(GPU_FRAMEBUFFER_NATIVE_HEIGHT * dstScale + gap);
hyb_ratio1 = MIN(hyb_hratio1, hyb_vratio1);
gint w_rem = daW - GPU_FRAMEBUFFER_NATIVE_WIDTH * dstScale / 2.0 * hyb_ratio1;
hyb_hratio2 = (gdouble)w_rem / (gdouble)(GPU_FRAMEBUFFER_NATIVE_WIDTH * dstScale / 2);
hyb_vratio2 = (gdouble)daH / (gdouble)(GPU_FRAMEBUFFER_NATIVE_HEIGHT * dstScale / 2);
hyb_ratio2 = MIN(hyb_hratio2, hyb_vratio2);
imgW = GPU_FRAMEBUFFER_NATIVE_WIDTH * dstScale / 2 * (1.0 + hyb_ratio2 / hyb_ratio1);
imgH = GPU_FRAMEBUFFER_NATIVE_HEIGHT * dstScale + gap;
draw_params.hybridBigScreenScale = hyb_ratio2 / hyb_ratio1;
}
/* Drawing callback */
static gboolean ExposeDrawingArea (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
@ -1408,51 +1494,96 @@ static gboolean ExposeDrawingArea (GtkWidget *widget, GdkEventExpose *event, gpo
u32* fbuf = video->GetDstBufferPtr();
gint dstW = video->GetDstWidth();
gint dstH = video->GetDstHeight();
gint dstScale = dstW * 2 / GPU_FRAMEBUFFER_NATIVE_WIDTH; // Actual scale * 2 to handle 1.5x filters
gint gap = nds_screen.orientation == ORIENT_VERTICAL ? nds_screen.gap_size * dstScale / 2 : 0;
gint imgW, imgH;
if (nds_screen.rotation_angle == 0 || nds_screen.rotation_angle == 180) {
imgW = screen_size[nds_screen.orientation].width * dstScale / 2;
imgH = screen_size[nds_screen.orientation].height * dstScale / 2 + gap;
} else {
imgH = screen_size[nds_screen.orientation].width * dstScale / 2;
imgW = screen_size[nds_screen.orientation].height * dstScale / 2 + gap;
if(draw_params.valid)
if(draw_params.daW != daW || draw_params.daH != daH || draw_params.dstW != dstW || draw_params.dstH != dstH)
draw_params.valid = false;
if(!draw_params.valid) {
draw_params.daW = daW;
draw_params.daH = daH;
draw_params.dstW = dstW;
draw_params.dstH = dstH;
draw_params.dstScale = dstW * 2 / GPU_FRAMEBUFFER_NATIVE_WIDTH; // Actual scale * 2 to handle 1.5x filters
draw_params.gap = ((nds_screen.orientation == ORIENT_VERTICAL) || (nds_screen.orientation == ORIENT_HYBRID_EQUAL)
|| (nds_screen.orientation == ORIENT_HYBRID_VERTICAL)) ? nds_screen.gap_size * draw_params.dstScale / 2 : 0;
if(nds_screen.orientation == ORIENT_HYBRID_VERTICAL) {
if (nds_screen.rotation_angle == 0 || nds_screen.rotation_angle == 180)
calc_hybrid_vertical_params(daW, daH, draw_params.gap, draw_params.dstScale, draw_params.imgW, draw_params.imgH);
else
calc_hybrid_vertical_params(daH, daW, draw_params.gap, draw_params.dstScale, draw_params.imgH, draw_params.imgW);
}
else {
if (nds_screen.rotation_angle == 0 || nds_screen.rotation_angle == 180) {
draw_params.imgW = screen_size[nds_screen.orientation].width * draw_params.dstScale / 2;
draw_params.imgH = screen_size[nds_screen.orientation].height * draw_params.dstScale / 2 + draw_params.gap;
if(nds_screen.orientation == ORIENT_HYBRID_EQUAL)
draw_params.imgW = (draw_params.imgW * 2.0/3.0) * (double)draw_params.imgH /
(double)(draw_params.imgH - draw_params.gap) + draw_params.imgW / 3.0;
} else {
draw_params.imgH = screen_size[nds_screen.orientation].width * draw_params.dstScale / 2;
draw_params.imgW = screen_size[nds_screen.orientation].height * draw_params.dstScale / 2 + draw_params.gap;
if(nds_screen.orientation == ORIENT_HYBRID_EQUAL)
draw_params.imgH = (draw_params.imgH * 2.0/3.0) * (double)draw_params.imgW /
(double)(draw_params.imgW - draw_params.gap) + draw_params.imgH / 3.0;
}
if(nds_screen.orientation == ORIENT_HYBRID_EQUAL)
draw_params.hybridBigScreenScale = (double)(dstH + draw_params.gap) / (double)(dstH / 2);
}
// Calculate scale to fit display area to window
gfloat hratio = (gfloat)daW / (gfloat)draw_params.imgW;
gfloat vratio = (gfloat)daH / (gfloat)draw_params.imgH;
draw_params.ratio = MIN(hratio, vratio);
draw_params.gapOffset = 0;
switch(nds_screen.orientation) {
case ORIENT_HYBRID_EQUAL:
draw_params.gapOffset = dstW * ((gdouble)(dstH + draw_params.gap) / (dstH / 2.0));
break;
case ORIENT_HYBRID_VERTICAL:
draw_params.gapOffset = dstW * draw_params.hybridBigScreenScale;
break;
}
draw_params.valid = true;
}
// Calculate scale to fit display area to window
gfloat hratio = (gfloat)daW / (gfloat)imgW;
gfloat vratio = (gfloat)daH / (gfloat)imgH;
hratio = MIN(hratio, vratio);
vratio = hratio;
GdkDrawingContext *context = gdk_window_begin_draw_frame(window, gdk_window_get_clip_region(window));
cairo_t* cr = gdk_drawing_context_get_cairo_context(context);
// Scale to window size at center of area
cairo_translate(cr, daW / 2, daH / 2);
cairo_scale(cr, hratio, vratio);
cairo_scale(cr, draw_params.ratio, draw_params.ratio);
// Rotate area
cairo_rotate(cr, M_PI / 180 * nds_screen.rotation_angle);
// Translate area to top-left corner
if (nds_screen.rotation_angle == 0 || nds_screen.rotation_angle == 180) {
cairo_translate(cr, -imgW / 2, -imgH / 2);
cairo_translate(cr, -draw_params.imgW / 2, -draw_params.imgH / 2);
} else {
cairo_translate(cr, -imgH / 2, -imgW / 2);
cairo_translate(cr, -draw_params.imgH / 2, -draw_params.imgW / 2);
}
// Draw both screens
drawTopScreen(cr, fbuf, dstW, dstH / 2, gap, nds_screen.rotation_angle, nds_screen.swap, nds_screen.orientation);
drawBottomScreen(cr, fbuf + dstW * dstH / 2, dstW, dstH / 2, gap, nds_screen.rotation_angle, nds_screen.swap, nds_screen.orientation);
drawTopScreen(cr, fbuf, dstW, dstH / 2, draw_params.gap, nds_screen.rotation_angle, nds_screen.swap, nds_screen.orientation);
drawBottomScreen(cr, fbuf + dstW * dstH / 2, dstW, dstH / 2, draw_params.gap, nds_screen.rotation_angle, nds_screen.swap, nds_screen.orientation);
// Draw gap
cairo_set_source_rgb(cr, 0.3, 0.3, 0.3);
cairo_rectangle(cr, 0, dstH / 2, dstW, gap);
cairo_rectangle(cr, draw_params.gapOffset, dstH / 2, dstW, draw_params.gap);
cairo_fill(cr);
// Complete the touch transformation matrix
cairo_matrix_scale(&nds_screen.topscreen_matrix, (double)dstScale / 2, (double)dstScale / 2);
cairo_matrix_scale(&nds_screen.topscreen_matrix, (double)draw_params.dstScale / 2, (double)draw_params.dstScale / 2);
cairo_matrix_invert(&nds_screen.topscreen_matrix);
cairo_matrix_scale(&nds_screen.touch_matrix, (double)dstScale / 2, (double)dstScale / 2);
cairo_matrix_scale(&nds_screen.touch_matrix, (double)draw_params.dstScale / 2, (double)draw_params.dstScale / 2);
cairo_matrix_invert(&nds_screen.touch_matrix);
if((nds_screen.orientation == ORIENT_HYBRID_EQUAL) || (nds_screen.orientation == ORIENT_HYBRID_VERTICAL)) {
cairo_matrix_scale(&nds_screen.topscreen_matrix_hybrid, (double)draw_params.dstScale / 2, (double)draw_params.dstScale / 2);
cairo_matrix_invert(&nds_screen.topscreen_matrix_hybrid);
cairo_matrix_scale(&nds_screen.touch_matrix_hybrid, (double)draw_params.dstScale / 2, (double)draw_params.dstScale / 2);
cairo_matrix_invert(&nds_screen.touch_matrix_hybrid);
}
gdk_window_end_draw_frame(window, context);
draw_count++;
@ -1476,12 +1607,17 @@ static void RedrawScreen() {
/////////////////////////////// KEYS AND STYLUS UPDATE ///////////////////////////////////////
static bool move_started_in_big_screen = false;
static bool hud_move_started_in_big_screen = false;
#ifdef HAVE_LIBAGG
static gboolean rotoscaled_hudedit(gint x, gint y, gboolean start)
{
double devX, devY;
gint X, Y, topX = -1, topY = -1, botX = -1, botY = -1;
gint X, Y, topX = -1, topY = -1, botX = -1, botY = -1, hybTopX = -1, hybTopY = -1, hybBotX = -1, hybBotY = -1;
static gint startScreen = 0;
bool hybrid = ((nds_screen.orientation == ORIENT_HYBRID_EQUAL) || (nds_screen.orientation == ORIENT_HYBRID_VERTICAL)) &&
(draw_params.hybridBigScreenScale > 0.0);
if (nds_screen.orientation != ORIENT_SINGLE || !nds_screen.swap) {
devX = x;
@ -1489,6 +1625,14 @@ static gboolean rotoscaled_hudedit(gint x, gint y, gboolean start)
cairo_matrix_transform_point(&nds_screen.topscreen_matrix, &devX, &devY);
topX = devX * gpu_scale_factor;
topY = devY * gpu_scale_factor;
if(hybrid && !nds_screen.swap) {
devX = x;
devY = y;
cairo_matrix_transform_point(&nds_screen.topscreen_matrix_hybrid, &devX, &devY);
hybTopX = devX * gpu_scale_factor;
hybTopY = devY * gpu_scale_factor;
}
}
if (nds_screen.orientation != ORIENT_SINGLE || nds_screen.swap) {
@ -1497,23 +1641,57 @@ static gboolean rotoscaled_hudedit(gint x, gint y, gboolean start)
cairo_matrix_transform_point(&nds_screen.touch_matrix, &devX, &devY);
botX = devX * gpu_scale_factor;
botY = devY * gpu_scale_factor;
if(hybrid && nds_screen.swap) {
devX = x;
devY = y;
cairo_matrix_transform_point(&nds_screen.touch_matrix_hybrid, &devX, &devY);
hybBotX = devX * gpu_scale_factor;
hybBotY = devY * gpu_scale_factor;
}
}
if (topX >= 0 && topY >= 0 && topX < real_framebuffer_width && topY < real_framebuffer_height) {
X = topX;
Y = topY + (nds_screen.swap ? real_framebuffer_height : 0);
Y = topY + (osd->swapScreens ? real_framebuffer_height : 0);
startScreen = 0;
if(start)
hud_move_started_in_big_screen = false;
} else if (hybTopX >= 0 && hybTopY >= 0 && hybTopX < real_framebuffer_width && hybTopY < real_framebuffer_height) {
X = hybTopX;
Y = hybTopY;
startScreen = 0;
if(start)
hud_move_started_in_big_screen = true;
} else if (botX >= 0 && botY >= 0 && botX < real_framebuffer_width && botY < real_framebuffer_height) {
X = botX;
Y = botY + (nds_screen.swap ? 0 : real_framebuffer_height);
Y = botY + (osd->swapScreens ? 0 : real_framebuffer_height);
startScreen = 1;
if(start)
hud_move_started_in_big_screen = false;
} else if (hybBotX >= 0 && hybBotY >= 0 && hybBotX < real_framebuffer_width && hybBotY < real_framebuffer_height) {
X = hybBotX;
Y = hybBotY + real_framebuffer_height;
startScreen = 1;
if(start)
hud_move_started_in_big_screen = true;
} else if (!start) {
if (startScreen == 0) {
X = CLAMP(topX, 0, real_framebuffer_width-1);
Y = CLAMP(topY, 0, real_framebuffer_height-1) + (nds_screen.swap ? real_framebuffer_height : 0);
if(!hud_move_started_in_big_screen || !hybrid) {
X = CLAMP(topX, 0, real_framebuffer_width-1);
Y = CLAMP(topY, 0, real_framebuffer_height-1) + (osd->swapScreens ? real_framebuffer_height : 0);
} else {
X = CLAMP(hybTopX, 0, real_framebuffer_width-1);
Y = CLAMP(hybTopY, 0, real_framebuffer_height-1);
}
} else {
X = CLAMP(botX, 0, real_framebuffer_width-1);
Y = CLAMP(botY, 0, real_framebuffer_height-1) + (nds_screen.swap ? 0 : real_framebuffer_height);
if(!hud_move_started_in_big_screen || !hybrid) {
X = CLAMP(botX, 0, real_framebuffer_width-1);
Y = CLAMP(botY, 0, real_framebuffer_height-1) + (osd->swapScreens ? 0 : real_framebuffer_height);
} else {
X = CLAMP(hybBotX, 0, real_framebuffer_width-1);
Y = CLAMP(hybBotY, 0, real_framebuffer_height-1) + real_framebuffer_height;
}
}
} else {
LOG("TopX=%d, TopY=%d, BotX=%d, BotY=%d\n", topX, topY, botX, botY);
@ -1545,13 +1723,33 @@ static gboolean rotoscaled_touchpos(gint x, gint y, gboolean start)
LOG("X=%d, Y=%d\n", X, Y);
if (!start || (X >= 0 && Y >= 0 && X < 256 && Y < 192)) {
if ((!start && !move_started_in_big_screen) || (X >= 0 && Y >= 0 && X < 256 && Y < 192)) {
if(start)
move_started_in_big_screen = false;
EmuX = CLAMP(X, 0, 255);
EmuY = CLAMP(Y, 0, 191);
NDS_setTouchPos(EmuX, EmuY);
return TRUE;
}
if((((nds_screen.orientation == ORIENT_HYBRID_EQUAL) ||
(nds_screen.orientation == ORIENT_HYBRID_VERTICAL)) && (draw_params.hybridBigScreenScale > 0.0)) && (nds_screen.swap)) {
devX = x;
devY = y;
cairo_matrix_transform_point(&nds_screen.touch_matrix_hybrid, &devX, &devY);
X = devX;
Y = devY;
if ((!start && move_started_in_big_screen) || (X >= 0 && Y >= 0 && X < 256 && Y < 192)) {
if(start)
move_started_in_big_screen = true;
EmuX = CLAMP(X, 0, 255);
EmuY = CLAMP(Y, 0, 191);
NDS_setTouchPos(EmuX, EmuY);
return TRUE;
}
}
return FALSE;
}
@ -3450,7 +3648,7 @@ common_gtk_main(GApplication *app, gpointer user_data)
}
g_simple_action_set_state(G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(app), "winsize")), g_variant_new_string(string.c_str()));
if (config.view_orient < ORIENT_VERTICAL || config.view_orient > ORIENT_SINGLE) {
if (config.view_orient < ORIENT_VERTICAL || config.view_orient > ORIENT_HYBRID_VERTICAL) {
config.view_orient = ORIENT_VERTICAL;
}
nds_screen.orientation = (orientation_enum)config.view_orient.get();
@ -3464,6 +3662,12 @@ common_gtk_main(GApplication *app, gpointer user_data)
case ORIENT_SINGLE:
string = "single";
break;
case ORIENT_HYBRID_EQUAL:
string = "hybrid_equal";
break;
case ORIENT_HYBRID_VERTICAL:
string = "hybrid_vertical";
break;
}
g_simple_action_set_state(G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(app), "orient")), g_variant_new_string(string.c_str()));
@ -3484,6 +3688,10 @@ common_gtk_main(GApplication *app, gpointer user_data)
nds_screen.gap_size = config.view_gap ? GAP_SIZE : 0;
nds_screen.swap = config.view_swap;
#ifdef HAVE_LIBAGG
bool hybrid = (nds_screen.orientation == ORIENT_HYBRID_EQUAL) || (nds_screen.orientation == ORIENT_HYBRID_VERTICAL);
osd->swapScreens = nds_screen.swap && !hybrid;
#endif
g_simple_action_set_state(G_SIMPLE_ACTION(g_action_map_lookup_action(G_ACTION_MAP(app), "swapscreens")), g_variant_new_boolean(config.view_swap));
builder = gtk_builder_new_from_resource("/org/desmume/DeSmuME/menu.ui");

View File

@ -254,6 +254,18 @@
<attribute name='action'>app.orient</attribute>
<attribute name='target'>single</attribute>
<attribute name='accel'>&lt;Control&gt;0</attribute>
</item>
<item>
<attribute name='label' translatable='yes'>Hybrid (_equal height)</attribute>
<attribute name='action'>app.orient</attribute>
<attribute name='target'>hybrid_equal</attribute>
<attribute name='accel'>&lt;Control&gt;3</attribute>
</item>
<item>
<attribute name='label' translatable='yes'>Hybrid (e_mphasize vertical)</attribute>
<attribute name='action'>app.orient</attribute>
<attribute name='target'>hybrid_vertical</attribute>
<attribute name='accel'>&lt;Control&gt;4</attribute>
</item>
</section>
<section>