Merge branch 'master' of https://github.com/libretro/RetroArch into discord_improvements

This commit is contained in:
Jesse Bryan 2018-08-18 02:12:56 -05:00
commit fd7d8d9424
32 changed files with 897 additions and 2154 deletions

View File

@ -1066,7 +1066,7 @@ static void command_event_init_controllers(void)
break;
}
if (set_controller && i < info->ports.size)
if (set_controller && info && i < info->ports.size)
{
pad.device = device;
pad.port = i;

View File

@ -1069,7 +1069,7 @@ bool config_overlay_enable_default(void)
static struct config_array_setting *populate_settings_array(settings_t *settings, int *size)
{
unsigned count = 0;
struct config_array_setting *tmp = (struct config_array_setting*)malloc((*size + 1) * sizeof(struct config_array_setting));
struct config_array_setting *tmp = (struct config_array_setting*)calloc(1, (*size + 1) * sizeof(struct config_array_setting));
/* Arrays */
SETTING_ARRAY("playlist_names", settings->arrays.playlist_names, false, NULL, true);
@ -1112,7 +1112,7 @@ static struct config_path_setting *populate_settings_path(settings_t *settings,
{
unsigned count = 0;
global_t *global = global_get_ptr();
struct config_path_setting *tmp = (struct config_path_setting*)malloc((*size + 1) * sizeof(struct config_path_setting));
struct config_path_setting *tmp = (struct config_path_setting*)calloc(1, (*size + 1) * sizeof(struct config_path_setting));
/* Paths */
#ifdef HAVE_XMB
@ -1308,7 +1308,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
SETTING_BOOL("video_vsync", &settings->bools.video_vsync, true, vsync, false);
SETTING_BOOL("video_hard_sync", &settings->bools.video_hard_sync, true, hard_sync, false);
SETTING_BOOL("video_black_frame_insertion", &settings->bools.video_black_frame_insertion, true, black_frame_insertion, false);
SETTING_BOOL("crt_switch_resolution", &settings->bools.crt_switch_resolution, true, crt_switch_resolution, false);
SETTING_BOOL("crt_switch_resolution", &settings->bools.crt_switch_resolution, true, crt_switch_resolution, false);
SETTING_BOOL("video_disable_composition", &settings->bools.video_disable_composition, true, disable_composition, false);
SETTING_BOOL("pause_nonactive", &settings->bools.pause_nonactive, true, pause_nonactive, false);
SETTING_BOOL("video_gpu_screenshot", &settings->bools.video_gpu_screenshot, true, gpu_screenshot, false);
@ -3027,6 +3027,8 @@ end:
free(array_settings);
if (path_settings)
free(path_settings);
if (size_settings)
free(size_settings);
free(tmp_str);
return ret;
}
@ -4431,7 +4433,7 @@ bool config_save_overrides(int override_type)
for (i = 0; i < (unsigned)path_settings_size; i++)
{
/* blacklist video_shader, better handled by shader presets*/
/* blacklist video_shader, better handled by shader presets*/
/* to-do: add setting to control blacklisting */
if (string_is_equal(path_settings[i].ident, "video_shader"))
continue;
@ -4536,6 +4538,8 @@ bool config_save_overrides(int override_type)
free(path_settings);
if (path_overrides)
free(path_overrides);
if (size_overrides)
free(size_overrides);
free(settings);
free(config_directory);
free(override_directory);

View File

@ -478,6 +478,36 @@ database_info_list_t *database_info_list_new(
if (!new_ptr)
{
if (db_info.bbfc_rating)
free(db_info.bbfc_rating);
if (db_info.cero_rating)
free(db_info.cero_rating);
if (db_info.description)
free(db_info.description);
if (db_info.edge_magazine_review)
free(db_info.edge_magazine_review);
if (db_info.elspa_rating)
free(db_info.elspa_rating);
if (db_info.enhancement_hw)
free(db_info.enhancement_hw);
if (db_info.esrb_rating)
free(db_info.esrb_rating);
if (db_info.franchise)
free(db_info.franchise);
if (db_info.genre)
free(db_info.genre);
if (db_info.name)
free(db_info.name);
if (db_info.origin)
free(db_info.origin);
if (db_info.pegi_rating)
free(db_info.pegi_rating);
if (db_info.publisher)
free(db_info.publisher);
if (db_info.rom_name)
free(db_info.rom_name);
if (db_info.serial)
free(db_info.serial);
database_info_list_free(database_info_list);
free(database_info);
free(database_info_list);

View File

@ -63,12 +63,14 @@ void create_gdi_context(HWND hwnd, bool *quit);
bool gdi_has_menu_frame(void);
#if !defined(__WINRT__)
bool win32_window_init(WNDCLASSEX *wndclass, bool fullscreen, const char *class_name);
void win32_set_style(MONITORINFOEX *current_mon, HMONITOR *hm_to_use,
unsigned *width, unsigned *height, bool fullscreen, bool windowed_full,
RECT *rect, RECT *mon_rect, DWORD *style);
#endif
#endif
void win32_monitor_from_window(void);

View File

@ -380,7 +380,7 @@ static bool gl_shader_init(gl_t *gl, const gfx_ctx_driver_t *ctx_driver,
video_shader_ctx_init_t init_data;
enum rarch_shader_type type = DEFAULT_SHADER_TYPE;
const char *shader_path = retroarch_get_shader_preset();
if (shader_path)
{
type = video_shader_parse_type(shader_path,
@ -839,7 +839,7 @@ static void gl_show_mouse(void *data, bool state)
static struct video_shader *gl_get_current_shader(void *data)
{
video_shader_ctx_t shader_info;
video_shader_ctx_t shader_info = {0};
video_shader_driver_direct_get_current_shader(&shader_info);
@ -1798,9 +1798,9 @@ static void *gl_init(const video_info_t *video,
}
if (!renderchain_gl_init_first(&gl->renderchain_driver,
&gl->renderchain_data))
&gl->renderchain_data))
{
RARCH_ERR("[GL]: Renderchain could not be initialized.\n");
RARCH_ERR("[GL]: Renderchain could not be initialized.\n");
goto error;
}

View File

@ -107,6 +107,7 @@ typedef struct gfx_ctx_wayland_data
#endif
} gfx_ctx_wayland_data_t;
static enum gfx_ctx_api wl_api = GFX_CTX_NONE;
#ifndef EGL_OPENGL_ES3_BIT_KHR
@ -354,9 +355,10 @@ static void touch_handle_down(void *data,
{
int i;
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
if (num_active_touches < MAX_TOUCHES)
{
for (i=0; i<MAX_TOUCHES; i++)
for (i = 0; i < MAX_TOUCHES; i++)
{
/* Use next empty slot */
if (!active_touch_positions[i].active)
@ -371,13 +373,13 @@ static void touch_handle_down(void *data,
}
}
}
static void reorder_touches()
static void reorder_touches(void)
{
int i, j;
if (num_active_touches == 0)
return;
for (i=0; i<MAX_TOUCHES; i++)
for (i = 0; i < MAX_TOUCHES; i++)
{
if (!active_touch_positions[i].active)
{
@ -410,7 +412,7 @@ static void touch_handle_up(void *data,
int i;
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
for (i=0; i<MAX_TOUCHES; i++)
for (i = 0; i < MAX_TOUCHES; i++)
{
if (active_touch_positions[i].active && active_touch_positions[i].id == id)
{
@ -421,7 +423,7 @@ static void touch_handle_up(void *data,
num_active_touches--;
}
}
reorder_touches(wl);
reorder_touches();
}
static void touch_handle_motion(void *data,
struct wl_touch *wl_touch,
@ -432,7 +434,8 @@ static void touch_handle_motion(void *data,
{
int i;
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
for (i=0; i<MAX_TOUCHES; i++)
for (i = 0; i < MAX_TOUCHES; i++)
{
if (active_touch_positions[i].active && active_touch_positions[i].id == id)
{
@ -453,7 +456,8 @@ static void touch_handle_cancel(void *data,
* since they were not ment for us anyway */
int i;
gfx_ctx_wayland_data_t *wl = (gfx_ctx_wayland_data_t*)data;
for (i=0; i<MAX_TOUCHES; i++)
for (i = 0; i < MAX_TOUCHES; i++)
{
active_touch_positions[i].active = false;
active_touch_positions[i].id = -1;
@ -1105,7 +1109,7 @@ static void *gfx_ctx_wl_init(video_frame_info_t *video_info, void *video_driver)
wl->cursor.default_cursor = wl_cursor_theme_get_cursor(wl->cursor.theme, "left_ptr");
num_active_touches = 0;
for (i=0;i<MAX_TOUCHES;i++)
for (i = 0;i < MAX_TOUCHES;i++)
{
active_touch_positions[i].active = false;
active_touch_positions[i].id = -1;

View File

@ -503,8 +503,8 @@ bool video_shader_resolve_parameters(config_file_t *conf,
char *line = NULL;
const char *path = shader->pass[i].source.path;
if (string_is_empty(path))
continue;
if (string_is_empty(path))
continue;
#if defined(HAVE_SLANG) && defined(HAVE_SPIRV_CROSS)
/* First try to use the more robust slang
@ -529,6 +529,9 @@ bool video_shader_resolve_parameters(config_file_t *conf,
line = (char*)malloc(4096 * sizeof(char));
line[0] = '\0';
/* even though the pass is set in the loop too, not all passes have parameters */
param->pass = i;
while (shader->num_parameters < ARRAY_SIZE(shader->parameters)
&& intfstream_gets(file, line, line_size))
{

View File

@ -99,29 +99,28 @@ static int16_t input_wl_lightgun_state(input_ctx_wayland_data_t *wl, unsigned id
return 0;
}
/* forward declaration */
bool wayland_context_gettouchpos(void *data, unsigned id,
unsigned* touch_x, unsigned* touch_y);
static void input_wl_touch_pool(void *data)
{
int id;
unsigned touch_x = 0;
unsigned touch_y = 0;
input_ctx_wayland_data_t *wl = (input_ctx_wayland_data_t*)data;
if (!wl)
return;
unsigned touch_x = 0;
unsigned touch_y = 0;
for (id=0; id<MAX_TOUCHES; id++)
for (id = 0; id < MAX_TOUCHES; id++)
{
if (wayland_context_gettouchpos(wl, id, &touch_x, &touch_y))
{
wl->touches[id].active = true;
}
else
{
wl->touches[id].active = false;
}
wl->touches[id].x = touch_x;
wl->touches[id].y = touch_y;
wl->touches[id].x = touch_x;
wl->touches[id].y = touch_y;
}
}

View File

@ -22,7 +22,7 @@
#include <file/nbio.h>
#if defined(_WIN32) && !defined(_XBOX)
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
#include <stdio.h>
#include <stdlib.h>

View File

@ -2505,15 +2505,18 @@ static uint8_t *rjpeg_load_jpeg_image(rjpeg__jpeg *z,
if (n >= 3)
{
uint8_t *y = coutput[0];
if (z->s->img_n == 3)
z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
else
for (i=0; i < z->s->img_x; ++i)
{
out[0] = out[1] = out[2] = y[i];
out[3] = 255; /* not used if n==3 */
out += n;
}
if (y)
{
if (z->s->img_n == 3)
z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n);
else
for (i=0; i < z->s->img_x; ++i)
{
out[0] = out[1] = out[2] = y[i];
out[3] = 255; /* not used if n==3 */
out += n;
}
}
}
else
{

View File

@ -544,6 +544,7 @@ static int png_reverse_filter_init(const struct png_ihdr *ihdr,
if (pngp->pass_size > pngp->total_out)
{
free(pngp->data);
pngp->data = NULL;
return -1;
}
@ -721,6 +722,7 @@ static int png_reverse_filter_adam7_iterate(uint32_t **data_,
free(pngp->data);
pngp->data = NULL;
pngp->pass_width = 0;
pngp->pass_height = 0;
pngp->pass_size = 0;
@ -746,7 +748,10 @@ static int png_reverse_filter_adam7(uint32_t **data_,
return 0;
case IMAGE_PROCESS_ERROR:
if (pngp->data)
{
free(pngp->data);
pngp->data = NULL;
}
pngp->inflate_buf -= pngp->adam7_restore_buf_size;
pngp->adam7_restore_buf_size = 0;
return -1;

View File

@ -543,17 +543,20 @@ static void materialui_render_messagebox(materialui_handle_t *mui,
}
}
menu_display_set_alpha(body_bg_color, 1.0);
if (body_bg_color)
{
menu_display_set_alpha(body_bg_color, 1.0);
menu_display_draw_quad(
video_info,
x - longest_width / 2.0 - mui->margin * 2.0,
y - line_height / 2.0 - mui->margin * 2.0,
longest_width + mui->margin * 4.0,
line_height * list->size + mui->margin * 4.0,
width,
height,
&body_bg_color[0]);
menu_display_draw_quad(
video_info,
x - longest_width / 2.0 - mui->margin * 2.0,
y - line_height / 2.0 - mui->margin * 2.0,
longest_width + mui->margin * 4.0,
line_height * list->size + mui->margin * 4.0,
width,
height,
&body_bg_color[0]);
}
/* print each line */
for (i = 0; i < list->size; i++)

View File

@ -30,6 +30,7 @@ import android.util.Log;
public final class MainMenuActivity extends PreferenceActivity
{
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
public static String PACKAGE_NAME;
boolean checkPermissions = false;
public void showMessageOKCancel(String message, DialogInterface.OnClickListener onClickListener)
@ -181,7 +182,7 @@ public final class MainMenuActivity extends PreferenceActivity
retro.putExtra("SDCARD", Environment.getExternalStorageDirectory().getAbsolutePath());
retro.putExtra("DOWNLOADS", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());
retro.putExtra("SCREENSHOTS", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath());
String external = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/com.retroarch/files";
String external = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/" + PACKAGE_NAME + "/files";
retro.putExtra("EXTERNAL", external);
}
@ -190,6 +191,8 @@ public final class MainMenuActivity extends PreferenceActivity
{
super.onCreate(savedInstanceState);
PACKAGE_NAME = getPackageName();
// Bind audio stream to hardware controls.
setVolumeControlStream(AudioManager.STREAM_MUSIC);

View File

@ -14,4 +14,4 @@
# 'key.store' for the location of your keystore and
# 'key.alias' for the name of the key to use.
# The password will be asked during the build when you use the 'release' target.
source.dir=../phoenix/src

View File

@ -1,200 +0,0 @@
package com.retroarch.browser.mainmenu;
import com.retroarch.browser.preferences.util.UserPreferences;
import com.retroarch.browser.retroactivity.RetroActivityFuture;
import com.retroarch.browser.retroactivity.RetroActivityPast;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.provider.Settings;
import java.util.List;
import java.util.ArrayList;
import android.content.pm.PackageManager;
import android.Manifest;
import android.content.DialogInterface;
import android.app.AlertDialog;
import android.util.Log;
/**
* {@link PreferenceActivity} subclass that provides all of the
* functionality of the main menu screen.
*/
public final class MainMenuActivity extends PreferenceActivity
{
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;
boolean checkPermissions = false;
public void showMessageOKCancel(String message, DialogInterface.OnClickListener onClickListener)
{
new AlertDialog.Builder(this).setMessage(message)
.setPositiveButton("OK", onClickListener).setCancelable(false)
.setNegativeButton("Cancel", null).create().show();
}
private boolean addPermission(List<String> permissionsList, String permission)
{
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED)
{
permissionsList.add(permission);
// Check for Rationale Option
if (!shouldShowRequestPermissionRationale(permission))
return false;
}
return true;
}
public void checkRuntimePermissions()
{
if (android.os.Build.VERSION.SDK_INT >= 23)
{
// Android 6.0+ needs runtime permission checks
List<String> permissionsNeeded = new ArrayList<String>();
final List<String> permissionsList = new ArrayList<String>();
if (!addPermission(permissionsList, Manifest.permission.READ_EXTERNAL_STORAGE))
permissionsNeeded.add("Read External Storage");
if (!addPermission(permissionsList, Manifest.permission.WRITE_EXTERNAL_STORAGE))
permissionsNeeded.add("Write External Storage");
if (permissionsList.size() > 0)
{
checkPermissions = true;
if (permissionsNeeded.size() > 0)
{
// Need Rationale
Log.i("MainMenuActivity", "Need to request external storage permissions.");
String message = "You need to grant access to " + permissionsNeeded.get(0);
for (int i = 1; i < permissionsNeeded.size(); i++)
message = message + ", " + permissionsNeeded.get(i);
showMessageOKCancel(message,
new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
if (which == AlertDialog.BUTTON_POSITIVE)
{
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
Log.i("MainMenuActivity", "User accepted request for external storage permissions.");
}
}
});
}
else
{
requestPermissions(permissionsList.toArray(new String[permissionsList.size()]),
REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS);
Log.i("MainMenuActivity", "Requested external storage permissions.");
}
}
}
if (!checkPermissions)
{
finalStartup();
}
}
public void finalStartup()
{
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Intent retro;
if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB))
{
retro = new Intent(this, RetroActivityFuture.class);
}
else
{
retro = new Intent(this, RetroActivityPast.class);
}
retro.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startRetroActivity(
retro,
null,
prefs.getString("libretro_path", getApplicationInfo().dataDir + "/cores/"),
UserPreferences.getDefaultConfigPath(this),
Settings.Secure.getString(getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD),
getApplicationInfo().dataDir,
getApplicationInfo().sourceDir);
startActivity(retro);
finish();
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)
{
switch (requestCode)
{
case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS:
for (int i = 0; i < permissions.length; i++)
{
if(grantResults[i] == PackageManager.PERMISSION_GRANTED)
{
Log.i("MainMenuActivity", "Permission: " + permissions[i] + " was granted.");
}
else
{
Log.i("MainMenuActivity", "Permission: " + permissions[i] + " was not granted.");
}
}
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
break;
}
finalStartup();
}
public static void startRetroActivity(Intent retro, String contentPath, String corePath,
String configFilePath, String imePath, String dataDirPath, String dataSourcePath)
{
if (contentPath != null) {
retro.putExtra("ROM", contentPath);
}
retro.putExtra("LIBRETRO", corePath);
retro.putExtra("CONFIGFILE", configFilePath);
retro.putExtra("IME", imePath);
retro.putExtra("DATADIR", dataDirPath);
retro.putExtra("APK", dataSourcePath);
retro.putExtra("SDCARD", Environment.getExternalStorageDirectory().getAbsolutePath());
retro.putExtra("DOWNLOADS", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath());
retro.putExtra("SCREENSHOTS", Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).getAbsolutePath());
String external = Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android/data/com.retroarch.aarch64/files";
retro.putExtra("EXTERNAL", external);
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Bind audio stream to hardware controls.
setVolumeControlStream(AudioManager.STREAM_MUSIC);
UserPreferences.updateConfigFile(this);
checkRuntimePermissions();
}
}

View File

@ -1,281 +0,0 @@
package com.retroarch.browser.preferences.util;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import android.util.Log;
/**
* Represents a configuration file that works off of a key-value pair
* in the form [key name] = "[value]".
*/
public final class ConfigFile
{
// Map containing all of the key-value pairs.
private final HashMap<String, String> map = new HashMap<String, String>();
/**
* Constructor
*/
public ConfigFile()
{
}
/**
* Constructor
*
* @param filePath The path to the configuration file to open.
*/
public ConfigFile(String filePath)
{
if (filePath == null)
throw new IllegalArgumentException("filePath cannot be null.");
try
{
open(filePath);
}
catch (IOException ioe)
{
Log.e("ConfigFile", "Stream reading the configuration file was suddenly closed for an unknown reason.");
}
}
/**
* Parses a configuration file from the given stream
* and appends the parsed values to the key-value map.
*
* @param stream The {@link InputStream} containing the configuration file to parse.
*/
public void append(InputStream stream) throws IOException
{
BufferedReader br = new BufferedReader(new InputStreamReader(stream));
String line;
while ((line = br.readLine()) != null)
parseLine(line);
br.close();
}
/**
* Opens a configuration file given by configPath
* and parses all of its key-value pairs, adding
* them to the key-value map.
*
* @param configPath Path to the configuration file to parse.
*/
public void open(String configPath) throws IOException
{
clear();
append(new FileInputStream(configPath));
}
private void parseLine(String line)
{
String[] tokens = line.split("=", 2);
if (tokens.length < 2)
return;
for (int i = 0; i < tokens.length; i++)
tokens[i] = tokens[i].trim();
String key = tokens[0];
String value = tokens[1];
if (value.startsWith("\""))
value = value.substring(1, value.lastIndexOf('\"'));
else
value = value.split(" ")[0];
if (value.length() > 0)
map.put(key, value);
}
/**
* Clears the key-value map of all currently set keys and values.
*/
public void clear()
{
map.clear();
}
/**
* Writes the currently set key-value pairs to
*
* @param path The path to save the
*
* @throws IOException
*/
public void write(String path) throws IOException
{
PrintWriter writer = new PrintWriter(path);
for (Map.Entry<String, String> entry : map.entrySet())
{
writer.println(entry.getKey() + " = \"" + entry.getValue() + "\"");
}
writer.close();
}
/**
* Checks if a key exists in the {@link HashMap}
* backing this ConfigFile instance.
*
* @param key The key to check for.
*
* @return true if the key exists in the HashMap backing
* this ConfigFile; false if it doesn't.
*/
public boolean keyExists(String key)
{
return map.containsKey(key);
}
/**
* Sets a key to the given String value.
*
* @param key The key to set the String value to.
* @param value The String value to set to the key.
*/
public void setString(String key, String value)
{
map.put(key, value);
}
/**
* Sets a key to the given boolean value.
*
* @param key The key to set the boolean value to.
* @param value The boolean value to set to the key.
*/
public void setBoolean(String key, boolean value)
{
map.put(key, Boolean.toString(value));
}
/**
* Sets a key to the given Integer value.
*
* @param key The key to set the Integer value to.
* @param value The Integer value to set to the key.
*/
public void setInt(String key, int value)
{
map.put(key, Integer.toString(value));
}
/**
* Sets a key to the given double value.
*
* @param key The key to set the double value to.
* @param value The double value to set to the key.
*/
public void setDouble(String key, double value)
{
map.put(key, Double.toString(value));
}
/**
* Sets a key to the given float value.
*
* @param key The key to set the float value to.
* @param value The float value to set to the key.
*/
public void setFloat(String key, float value)
{
map.put(key, Float.toString(value));
}
/**
* Gets the String value associated with the given key.
*
* @param key The key to get the String value from.
*
* @return the String object associated with the given key.
*/
public String getString(String key)
{
String ret = map.get(key);
if (ret != null)
return ret;
else
return null;
}
/**
* Gets the Integer value associated with the given key.
*
* @param key The key to get the Integer value from.
*
* @return the Integer value associated with the given key.
*/
public int getInt(String key)
{
String str = getString(key);
if (str != null)
return Integer.parseInt(str);
else
throw new IllegalArgumentException("Config key '" + key + "' is invalid.");
}
/**
* Gets the double value associated with the given key.
*
* @param key The key to get the double value from.
*
* @return the double value associated with the given key.
*/
public double getDouble(String key)
{
String str = getString(key);
if (str != null)
return Double.parseDouble(str);
else
throw new IllegalArgumentException("Config key '" + key + "' is invalid.");
}
/**
* Gets the float value associated with the given key.
*
* @param key The key to get the float value from.
*
* @return the float value associated with the given key.
*/
public float getFloat(String key)
{
String str = getString(key);
if (str != null)
return Float.parseFloat(str);
else
throw new IllegalArgumentException("Config key '" + key + "' is invalid.");
}
/**
* Gets the boolean value associated with the given key.
*
* @param key The key to get the boolean value from.
*
* @return the boolean value associated with the given key.
*/
public boolean getBoolean(String key)
{
String str = getString(key);
if (str != null)
return Boolean.parseBoolean(str);
else
throw new IllegalArgumentException("Config key '" + key + "' is invalid.");
}
}

View File

@ -1,300 +0,0 @@
package com.retroarch.browser.preferences.util;
import java.io.File;
import java.io.IOException;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Build;
import android.preference.PreferenceManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.util.Log;
/**
* Utility class for retrieving, saving, or loading preferences.
*/
public final class UserPreferences
{
// Logging tag.
private static final String TAG = "UserPreferences";
// Disallow explicit instantiation.
private UserPreferences()
{
}
/**
* Retrieves the path to the default location of the libretro config.
*
* @param ctx the current {@link Context}
*
* @return the path to the default location of the libretro config.
*/
public static String getDefaultConfigPath(Context ctx)
{
// Internal/External storage dirs.
final String internal = ctx.getFilesDir().getAbsolutePath();
String external = null;
// Get the App's external storage folder
final String state = android.os.Environment.getExternalStorageState();
if (android.os.Environment.MEDIA_MOUNTED.equals(state)) {
File extsd = ctx.getExternalFilesDir(null);
external = extsd.getAbsolutePath();
}
// Native library directory and data directory for this front-end.
final String dataDir = ctx.getApplicationInfo().dataDir;
final String coreDir = dataDir + "/cores/";
// Get libretro name and path
final SharedPreferences prefs = getPreferences(ctx);
final String libretro_path = prefs.getString("libretro_path", coreDir);
// Check if global config is being used. Return true upon failure.
final boolean globalConfigEnabled = prefs.getBoolean("global_config_enable", true);
String append_path;
// If we aren't using the global config.
if (!globalConfigEnabled && !libretro_path.equals(coreDir))
{
String sanitized_name = sanitizeLibretroPath(libretro_path);
append_path = File.separator + sanitized_name + ".cfg";
}
else // Using global config.
{
append_path = File.separator + "retroarch.cfg";
}
if (external != null)
{
String confPath = external + append_path;
if (new File(confPath).exists())
return confPath;
}
else if (internal != null)
{
String confPath = internal + append_path;
if (new File(confPath).exists())
return confPath;
}
else
{
String confPath = "/mnt/extsd" + append_path;
if (new File(confPath).exists())
return confPath;
}
// Config file does not exist. Create empty one.
// emergency fallback
String new_path = "/mnt/sd" + append_path;
if (external != null)
new_path = external + append_path;
else if (internal != null)
new_path = internal + append_path;
else if (dataDir != null)
new_path = dataDir + append_path;
try {
new File(new_path).createNewFile();
}
catch (IOException e)
{
Log.e(TAG, "Failed to create config file to: " + new_path);
}
return new_path;
}
/**
* Updates the libretro configuration file
* with new values if settings have changed.
*
* @param ctx the current {@link Context}.
*/
public static void updateConfigFile(Context ctx)
{
String path = getDefaultConfigPath(ctx);
ConfigFile config = new ConfigFile(path);
Log.i(TAG, "Writing config to: " + path);
final String dataDir = ctx.getApplicationInfo().dataDir;
final String coreDir = dataDir + "/cores/";
final SharedPreferences prefs = getPreferences(ctx);
config.setString("libretro_directory", coreDir);
config.setInt("audio_out_rate", getOptimalSamplingRate(ctx));
try
{
int version = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0).versionCode;
final String dst_path = dataDir;
final String dst_path_subdir = "assets";
Log.i(TAG, "dst dir is: " + dst_path);
Log.i(TAG, "dst subdir is: " + dst_path_subdir);
config.setBoolean("log_verbosity", true);
config.setString("bundle_assets_src_path", ctx.getApplicationInfo().sourceDir);
config.setString("bundle_assets_dst_path", dst_path);
config.setString("bundle_assets_dst_path_subdir", dst_path_subdir);
config.setInt("bundle_assets_extract_version_current", version);
}
catch (NameNotFoundException ignored)
{
}
// Refactor this entire mess and make this usable for per-core config
if (Build.VERSION.SDK_INT >= 17 && prefs.getBoolean("audio_latency_auto", true))
{
config.setInt("audio_block_frames", getLowLatencyBufferSize(ctx));
}
try
{
config.write(path);
}
catch (IOException e)
{
Log.e(TAG, "Failed to save config file to: " + path);
}
}
private static void readbackString(ConfigFile cfg, SharedPreferences.Editor edit, String key)
{
if (cfg.keyExists(key))
edit.putString(key, cfg.getString(key));
else
edit.remove(key);
}
private static void readbackBool(ConfigFile cfg, SharedPreferences.Editor edit, String key)
{
if (cfg.keyExists(key))
edit.putBoolean(key, cfg.getBoolean(key));
else
edit.remove(key);
}
private static void readbackDouble(ConfigFile cfg, SharedPreferences.Editor edit, String key)
{
if (cfg.keyExists(key))
edit.putFloat(key, (float)cfg.getDouble(key));
else
edit.remove(key);
}
/*
private static void readbackFloat(ConfigFile cfg, SharedPreferences.Editor edit, String key)
{
if (cfg.keyExists(key))
edit.putFloat(key, cfg.getFloat(key));
else
edit.remove(key);
}
*/
/**
private static void readbackInt(ConfigFile cfg, SharedPreferences.Editor edit, String key)
{
if (cfg.keyExists(key))
edit.putInt(key, cfg.getInt(key));
else
edit.remove(key);
}
*/
/**
* Sanitizes a libretro core path.
*
* @param path The path to the libretro core.
*
* @return the sanitized libretro path.
*/
private static String sanitizeLibretroPath(String path)
{
String sanitized_name = path.substring(
path.lastIndexOf('/') + 1,
path.lastIndexOf('.'));
sanitized_name = sanitized_name.replace("neon", "");
sanitized_name = sanitized_name.replace("libretro_", "");
return sanitized_name;
}
/**
* Gets a {@link SharedPreferences} instance containing current settings.
*
* @param ctx the current {@link Context}.
*
* @return A SharedPreference instance containing current settings.
*/
public static SharedPreferences getPreferences(Context ctx)
{
return PreferenceManager.getDefaultSharedPreferences(ctx);
}
/**
* Gets the optimal sampling rate for low-latency audio playback.
*
* @param ctx the current {@link Context}.
*
* @return the optimal sampling rate for low-latency audio playback in Hz.
*/
@TargetApi(17)
private static int getLowLatencyOptimalSamplingRate(Context ctx)
{
AudioManager manager = (AudioManager) ctx.getSystemService(Context.AUDIO_SERVICE);
return Integer.parseInt(manager
.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE));
}
/**
* Gets the optimal buffer size for low-latency audio playback.
*
* @param ctx the current {@link Context}.
*
* @return the optimal output buffer size in decimal PCM frames.
*/
@TargetApi(17)
private static int getLowLatencyBufferSize(Context ctx)
{
AudioManager manager = (AudioManager) ctx.getSystemService(Context.AUDIO_SERVICE);
int buffersize = Integer.parseInt(manager
.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER));
Log.i(TAG, "Queried ideal buffer size (frames): " + buffersize);
return buffersize;
}
/**
* Gets the optimal audio sampling rate.
* <p>
* On Android 4.2+ devices this will retrieve the optimal low-latency sampling rate,
* since Android 4.2 adds support for low latency audio in general.
* <p>
* On other devices, it simply returns the regular optimal sampling rate
* as returned by the hardware.
*
* @param ctx The current {@link Context}.
*
* @return the optimal audio sampling rate in Hz.
*/
private static int getOptimalSamplingRate(Context ctx)
{
int ret;
if (Build.VERSION.SDK_INT >= 17)
ret = getLowLatencyOptimalSamplingRate(ctx);
else
ret = AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
Log.i(TAG, "Using sampling rate: " + ret + " Hz");
return ret;
}
}

View File

@ -1,225 +0,0 @@
package com.retroarch.browser.retroactivity;
import java.io.IOException;
import com.retroarch.browser.preferences.util.UserPreferences;
import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.graphics.SurfaceTexture;
import android.graphics.SurfaceTexture.OnFrameAvailableListener;
import android.hardware.Camera;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
//For Android 3.0 and up
/**
* Class which provides {@link Camera} functionality
* to {@link RetroActivityFuture}.
*/
@SuppressLint("NewApi")
public class RetroActivityCamera extends RetroActivityCommon
{
private Camera mCamera = null;
private long lastTimestamp = 0;
private SurfaceTexture texture;
private boolean updateSurface = true;
private boolean camera_service_running = false;
/**
* Executed when the {@link Camera}
* is staring to capture.
*/
public void onCameraStart()
{
if (camera_service_running)
return;
if (mCamera != null)
mCamera.startPreview();
camera_service_running = true;
}
/**
* Executed when the {@link Camera} is done capturing.
* <p>
* Note that this does not release the currently held
* {@link Camera} instance and must be freed by calling
* {@link #onCameraFree}
*/
public void onCameraStop()
{
if (!camera_service_running)
return;
if (mCamera != null)
mCamera.stopPreview();
camera_service_running = false;
}
/**
* Releases the currently held {@link Camera} instance.
*/
public void onCameraFree()
{
onCameraStop();
if (mCamera != null)
mCamera.release();
}
/**
* Initializes the camera for use.
*/
public void onCameraInit()
{
if (mCamera != null)
return;
mCamera = Camera.open();
}
/**
* Polls the camera for updates to the {@link SurfaceTexture}.
*
* @return true if polling was successful, false otherwise.
*/
public boolean onCameraPoll()
{
if (!camera_service_running)
return false;
if (texture == null)
{
Log.i("RetroActivity", "No texture");
return true;
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
{
if (updateSurface)
{
texture.updateTexImage();
}
long newTimestamp = texture.getTimestamp();
if (newTimestamp != lastTimestamp)
{
lastTimestamp = newTimestamp;
return true;
}
return false;
}
return true;
}
/**
* Initializes the {@link SurfaceTexture} used by the
* {@link Camera} with a given OpenGL texure ID.
*
* @param gl_texid texture ID to initialize the
* {@link SurfaceTexture} with.
*/
public void onCameraTextureInit(int gl_texid)
{
texture = new SurfaceTexture(gl_texid);
texture.setOnFrameAvailableListener(onCameraFrameAvailableListener);
}
/**
* Sets the {@link Camera} texture with the texture represented
* by the given OpenGL texture ID.
*
* @param gl_texid The texture ID representing the texture to set the camera to.
* @throws IOException If setting the texture fails.
*/
public void onCameraSetTexture(int gl_texid) throws IOException
{
if (texture == null)
onCameraTextureInit(gl_texid);
if (mCamera != null)
mCamera.setPreviewTexture(texture);
}
private final OnFrameAvailableListener onCameraFrameAvailableListener = new OnFrameAvailableListener()
{
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture)
{
updateSurface = true;
}
};
@Override
public void onCreate(Bundle savedInstanceState)
{
// Save the current setting for updates
SharedPreferences prefs = UserPreferences.getPreferences(this);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("CAMERA_UPDATES_ON", false);
edit.apply();
camera_service_running = false;
super.onCreate(savedInstanceState);
}
@Override
public void onPause()
{
// Save the current setting for updates
SharedPreferences prefs = UserPreferences.getPreferences(this);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("CAMERA_UPDATES_ON", camera_service_running);
edit.apply();
onCameraStop();
super.onPause();
}
@Override
public void onResume()
{
SharedPreferences prefs = UserPreferences.getPreferences(this);
SharedPreferences.Editor edit = prefs.edit();
/*
* Get any previous setting for camera updates
* Gets "false" if an error occurs
*/
if (prefs.contains("CAMERA_UPDATES_ON"))
{
camera_service_running = prefs.getBoolean("CAMERA_UPDATES_ON", false);
if (camera_service_running)
{
onCameraStart();
}
}
else // Otherwise, turn off camera updates
{
edit.putBoolean("CAMERA_UPDATES_ON", false);
edit.apply();
camera_service_running = false;
}
super.onResume();
}
@Override
public void onDestroy()
{
onCameraFree();
super.onDestroy();
}
@Override
public void onStop()
{
onCameraStop();
super.onStop();
}
}

View File

@ -1,141 +0,0 @@
package com.retroarch.browser.retroactivity;
import com.retroarch.browser.preferences.util.UserPreferences;
import android.annotation.TargetApi;
import android.content.res.Configuration;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.app.UiModeManager;
import android.os.BatteryManager;
import android.os.Build;
import android.os.PowerManager;
import android.util.Log;
import java.util.concurrent.CountDownLatch;
/**
* Class which provides common methods for RetroActivity related classes.
*/
public class RetroActivityCommon extends RetroActivityLocation
{
public static int FRONTEND_POWERSTATE_NONE = 0;
public static int FRONTEND_POWERSTATE_NO_SOURCE = 1;
public static int FRONTEND_POWERSTATE_CHARGING = 2;
public static int FRONTEND_POWERSTATE_CHARGED = 3;
public static int FRONTEND_POWERSTATE_ON_POWER_SOURCE = 4;
public boolean sustainedPerformanceMode = true;
// Exiting cleanly from NDK seems to be nearly impossible.
// Have to use exit(0) to avoid weird things happening, even with runOnUiThread() approaches.
// Use a separate JNI function to explicitly trigger the readback.
public void onRetroArchExit()
{
finish();
}
@TargetApi(24)
public void setSustainedPerformanceMode(boolean on)
{
sustainedPerformanceMode = on;
if (Build.VERSION.SDK_INT >= 24) {
if (isSustainedPerformanceModeSupported()) {
final CountDownLatch latch = new CountDownLatch(1);
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i("RetroActivity", "setting sustained performance mode to " + sustainedPerformanceMode);
getWindow().setSustainedPerformanceMode(sustainedPerformanceMode);
latch.countDown();
}
});
try {
latch.await();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
@TargetApi(24)
public boolean isSustainedPerformanceModeSupported()
{
boolean supported = false;
if (Build.VERSION.SDK_INT >= 24)
{
PowerManager powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
if (powerManager.isSustainedPerformanceModeSupported())
supported = true;
}
Log.i("RetroActivity", "isSustainedPerformanceModeSupported? " + supported);
return supported;
}
public int getBatteryLevel()
{
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
// This doesn't actually register anything (or need to) because we know this particular intent is sticky and we do not specify a BroadcastReceiver anyway
Intent batteryStatus = registerReceiver(null, ifilter);
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
float percent = ((float)level / (float)scale) * 100.0f;
Log.i("RetroActivity", "battery: level = " + level + ", scale = " + scale + ", percent = " + percent);
return (int)percent;
}
public int getPowerstate()
{
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
// This doesn't actually register anything (or need to) because we know this particular intent is sticky and we do not specify a BroadcastReceiver anyway
Intent batteryStatus = registerReceiver(null, ifilter);
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean hasBattery = batteryStatus.getBooleanExtra(BatteryManager.EXTRA_PRESENT, false);
boolean isCharging = (status == BatteryManager.BATTERY_STATUS_CHARGING);
boolean isCharged = (status == BatteryManager.BATTERY_STATUS_FULL);
int powerstate = FRONTEND_POWERSTATE_NONE;
if (isCharged)
powerstate = FRONTEND_POWERSTATE_CHARGED;
else if (isCharging)
powerstate = FRONTEND_POWERSTATE_CHARGING;
else if (!hasBattery)
powerstate = FRONTEND_POWERSTATE_NO_SOURCE;
else
powerstate = FRONTEND_POWERSTATE_ON_POWER_SOURCE;
Log.i("RetroActivity", "power state = " + powerstate);
return powerstate;
}
public boolean isAndroidTV()
{
Configuration config = getResources().getConfiguration();
UiModeManager uiModeManager = (UiModeManager)getSystemService(UI_MODE_SERVICE);
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION)
{
Log.i("RetroActivity", "isAndroidTV == true");
return true;
}
else
{
Log.i("RetroActivity", "isAndroidTV == false");
return false;
}
}
}

View File

@ -1,87 +0,0 @@
package com.retroarch.browser.retroactivity;
import android.view.View;
import android.view.WindowManager;
import android.content.Intent;
import android.content.Context;
import android.hardware.input.InputManager;
import android.os.Build;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public final class RetroActivityFuture extends RetroActivityCamera {
// If set to true then Retroarch will completely exit when it loses focus
private boolean quitfocus = false;
@Override
public void onResume() {
super.onResume();
setSustainedPerformanceMode(sustainedPerformanceMode);
if (Build.VERSION.SDK_INT >= 19) {
// Immersive mode
// Constants from API > 14
final int API_SYSTEM_UI_FLAG_LAYOUT_STABLE = 0x00000100;
final int API_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
final int API_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
final int API_SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
final int API_SYSTEM_UI_FLAG_IMMERSIVE_STICKY = 0x00001000;
View thisView = getWindow().getDecorView();
thisView.setSystemUiVisibility(API_SYSTEM_UI_FLAG_LAYOUT_STABLE
| API_SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| API_SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| API_SYSTEM_UI_FLAG_FULLSCREEN
| API_SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
// Check for Android UI specific parameters
Intent retro = getIntent();
String refresh = retro.getStringExtra("REFRESH");
// If REFRESH parameter is provided then try to set refreshrate accordingly
if(refresh != null) {
WindowManager.LayoutParams params = getWindow().getAttributes();
params.preferredRefreshRate = Integer.parseInt(refresh);
getWindow().setAttributes(params);
}
// If QUITFOCUS parameter is provided then enable that Retroarch quits when focus is lost
quitfocus = retro.hasExtra("QUITFOCUS");
// If HIDEMOUSE parameters is provided then hide the mourse cursor
// This requires NVIDIA Android extensions (available on NVIDIA Shield), if they are not
// available then nothing will be done
if (retro.hasExtra("HIDEMOUSE")) hideMouseCursor();
}
}
public void hideMouseCursor() {
// Check for NVIDIA extensions and minimum SDK version
Method mInputManager_setCursorVisibility;
try { mInputManager_setCursorVisibility =
InputManager.class.getMethod("setCursorVisibility", boolean.class);
}
catch (NoSuchMethodException ex) {
return; // Extensions were not available so do nothing
}
// Hide the mouse cursor
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
try { mInputManager_setCursorVisibility.invoke(inputManager, false); }
catch (InvocationTargetException ite) { }
catch (IllegalAccessException iae) { }
}
@Override
public void onStop() {
super.onStop();
// If QUITFOCUS parameter was set then completely exit Retroarch when focus is lost
if (quitfocus) System.exit(0);
}
}

View File

@ -1,105 +0,0 @@
package com.retroarch.browser.retroactivity;
import com.retroarch.browser.mainmenu.MainMenuActivity;
import android.content.Intent;
import android.util.Log;
public class RetroActivityIntent extends RetroActivityCommon {
private Intent pendingIntent = null;
private static final String TAG = "RetroArch";
@Override
public void onBackPressed()
{
Log.i("RetroActivity", "onBackKeyPressed");
Intent retro = new Intent(this, MainMenuActivity.class);
retro.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(retro);
}
@Override
public void onNewIntent(Intent intent)
{
Log.i("RetroActivity", "onNewIntent invoked.");
super.onNewIntent(intent);
setIntent(intent);
pendingIntent = intent;
}
/**
* Gets the ROM file specified in the pending intent.
*
* @return the ROM file specified in the pending intent.
*/
public String getPendingIntentFullPath()
{
return pendingIntent.getStringExtra("ROM");
}
/**
* Gets the specified path to the libretro core in the pending intent.
*
* @return the specified path to the libretro core in the pending intent.
*/
public String getPendingIntentLibretroPath()
{
return pendingIntent.getStringExtra("LIBRETRO");
}
/**
* Gets the path specified in the pending intent to the retroarch cfg file.
*
* @return the path specified in the pending intent to the retroarch cfg file.
*/
public String getPendingIntentConfigPath()
{
return pendingIntent.getStringExtra("CONFIGFILE");
}
public String getPendingIntentStorageLocation()
{
return pendingIntent.getStringExtra("SDCARD");
}
public String getPendingIntentDownloadLocation()
{
return pendingIntent.getStringExtra("DOWNLOADS");
}
public String getPendingIntentScreenshotsLocation()
{
return pendingIntent.getStringExtra("SCREENSHOTS");
}
/**
* Gets the specified IME in the pending intent.
*
* @return the specified IME in the pending intent.
*/
public String getPendingIntentIME()
{
return pendingIntent.getStringExtra("IME");
}
/**
* Checks whether or not a pending intent exists.
*
* @return true if a pending intent exists, false otherwise.
*/
public boolean hasPendingIntent()
{
if (pendingIntent == null)
return false;
return true;
}
/**
* Clears the current pending intent.
*/
public void clearPendingIntent()
{
pendingIntent = null;
}
}

View File

@ -1,316 +0,0 @@
package com.retroarch.browser.retroactivity;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.retroarch.browser.preferences.util.UserPreferences;
import android.app.NativeActivity;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
/**
* Class that implements location-based functionality for
* the {@link RetroActivityFuture} and {@link RetroActivityPast}
* activities.
*/
public class RetroActivityLocation extends NativeActivity
implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener
{
/* LOCATION VARIABLES */
private static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 0;
private LocationClient mLocationClient = null;
private Location mCurrentLocation;
// Define an object that holds accuracy and frequency parameters
LocationRequest mLocationRequest = null;
boolean mUpdatesRequested = false;
boolean locationChanged = false;
boolean location_service_running = false;
/**
* Called by Location Services when the request to connect the
* client finishes successfully. At this point, you can
* request the current location or start periodic updates
*/
@Override
public void onConnected(Bundle dataBundle)
{
if (mLocationClient == null)
return;
// Display the connection status
Toast.makeText(this, "Connected", Toast.LENGTH_SHORT).show();
location_service_running = true;
// If already requested, start periodic updates
if (mUpdatesRequested)
{
mLocationClient.requestLocationUpdates(mLocationRequest, this, null);
}
else
{
// Get last known location
mCurrentLocation = mLocationClient.getLastLocation();
locationChanged = true;
}
}
/**
* Called by Location Services if the connection to the
* location client drops because of an error.
*/
@Override
public void onDisconnected()
{
if (mLocationClient == null)
return;
// Display the connection status
Toast.makeText(this, "Disconnected. Please re-connect.", Toast.LENGTH_SHORT).show();
// If the client is connected
if (mLocationClient.isConnected())
{
/*
* Remove location updates for a listener.
* The current Activity is the listener, so
* the argument is "this".
*/
mLocationClient.removeLocationUpdates(this);
}
location_service_running = false;
}
/**
* Called by Location Services if the attempt to
* Location Services fails.
*/
@Override
public void onConnectionFailed(ConnectionResult connectionResult)
{
/*
* Google Play services can resolve some errors it detects.
* If the error has a resolution, try sending an Intent to
* start a Google Play services activity that can resolve
* error.
*/
if (connectionResult.hasResolution())
{
try
{
// Start an Activity that tries to resolve the error
connectionResult.startResolutionForResult(this, CONNECTION_FAILURE_RESOLUTION_REQUEST);
}
catch (IntentSender.SendIntentException e)
{
// Thrown if Google Play services cancelled the original PendingIntent
e.printStackTrace();
}
}
else
{
/*
* If no resolution is available, display a dialog to the
* user with the error.
*/
Log.e("Connection failed", "error code: " + connectionResult.getErrorCode());
}
}
/**
* Sets the update interval at which location-based updates
* should occur
*/
public void onLocationSetInterval(int update_interval_in_ms, int distance_interval)
{
// Use high accuracy
if (mLocationRequest == null)
return;
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
if (update_interval_in_ms == 0)
mLocationRequest.setInterval(5 * 1000); // 5 seconds
else
mLocationRequest.setInterval(update_interval_in_ms);
// Set the fastest update interval to 1 second
mLocationRequest.setFastestInterval(1000);
}
/**
* Initializing methods for location based functionality.
*/
public void onLocationInit()
{
/*
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
if (mLocationClient == null)
mLocationClient = new LocationClient(this, this, this);
// Start with updates turned off
mUpdatesRequested = false;
// Create the LocationRequest object
if (mLocationRequest == null)
mLocationRequest = LocationRequest.create();
onLocationSetInterval(0, 0);
}
/**
* Executed upon starting the {@link LocationClient}.
*/
public void onLocationStart()
{
if (mLocationClient == null)
return;
mUpdatesRequested = true;
// Connect the client.
mLocationClient.connect();
}
/**
* Free up location services resources.
*/
public void onLocationFree()
{
/* TODO/FIXME */
}
/**
* Executed upon stopping the location client.
* Does nothing if called when the client is not started.
*/
public void onLocationStop()
{
// Disconnecting the client invalidates it.
if (mLocationClient != null && mUpdatesRequested)
mLocationClient.disconnect();
}
/**
* Gets the latitude at the current location in degrees.
*
* @return the latitude at the current location.
*/
public double onLocationGetLatitude()
{
return mCurrentLocation.getLatitude();
}
/**
* Gets the longitude at the current location in degrees.
*
* @return the longitude at the current location.
*/
public double onLocationGetLongitude()
{
return mCurrentLocation.getLongitude();
}
/**
* Gets the horizontal accuracy of the current location
* in meters. (NOTE: There seems to be no vertical accuracy
* for a given location with the Android location API)
*
* @return the horizontal accuracy of the current position.
*/
public double onLocationGetHorizontalAccuracy()
{
return mCurrentLocation.getAccuracy();
}
/**
* Tells us whether the location listener callback has
* updated the current location since the last time
* we polled.
*
* @return true if location has changed, false if location has not changed.
*/
public boolean onLocationHasChanged()
{
boolean hasChanged = locationChanged;
// Reset flag
if (hasChanged)
locationChanged = false;
return hasChanged;
}
// Define the callback method that receives location updates
@Override
public void onLocationChanged(Location location)
{
if (!location_service_running)
return;
locationChanged = true;
mCurrentLocation = location;
// Report to the UI that the location was updated
String msg = "Updated Location: " + location.getLatitude() + ", " + location.getLongitude();
Log.i("RetroArch GPS", msg);
//Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
@Override
public void onPause()
{
// Save the current setting for updates
SharedPreferences prefs = UserPreferences.getPreferences(this);
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean("LOCATION_UPDATES_ON", mUpdatesRequested);
edit.apply();
super.onPause();
}
@Override
public void onResume()
{
SharedPreferences prefs = UserPreferences.getPreferences(this);
SharedPreferences.Editor edit = prefs.edit();
/*
* Get any previous setting for location updates
* Gets "false" if an error occurs
*/
if (prefs.contains("LOCATION_UPDATES_ON"))
{
mUpdatesRequested = prefs.getBoolean("LOCATION_UPDATES_ON", false);
if (mUpdatesRequested)
location_service_running = true;
}
else // Otherwise, turn off location updates
{
edit.putBoolean("LOCATION_UPDATES_ON", false);
edit.apply();
location_service_running = false;
}
super.onResume();
}
@Override
public void onStop()
{
onLocationStop();
super.onStop();
}
}

View File

@ -1,7 +0,0 @@
package com.retroarch.browser.retroactivity;
// For Android 2.3.x
public final class RetroActivityPast extends RetroActivityCommon
{
}

View File

@ -1569,6 +1569,10 @@ end:
free(content_ctx.name_ups);
if (content_ctx.directory_system)
free(content_ctx.directory_system);
if (content_ctx.directory_cache)
free(content_ctx.directory_cache);
if (content_ctx.valid_extensions)
free(content_ctx.valid_extensions);
if (!ret)
{
@ -1772,10 +1776,13 @@ void content_set_subsystem(unsigned idx)
pending_subsystem_id = idx;
strlcpy(pending_subsystem_ident,
subsystem->ident, sizeof(pending_subsystem_ident));
if (subsystem)
{
strlcpy(pending_subsystem_ident,
subsystem->ident, sizeof(pending_subsystem_ident));
pending_subsystem_rom_num = subsystem->num_roms;
pending_subsystem_rom_num = subsystem->num_roms;
}
RARCH_LOG("[subsystem] settings current subsytem to: %d(%s) roms: %d\n",
pending_subsystem_id, pending_subsystem_ident, pending_subsystem_rom_num);

View File

@ -6,7 +6,18 @@
#include <QStyleOption>
#include <QMimeData>
#include <QPainter>
#include <QAction>
#include <QFileInfo>
#include <QFileDialog>
#include <QMenu>
#include "filedropwidget.h"
#include "playlistentrydialog.h"
#include "../ui_qt.h"
extern "C" {
#include "../../../file_path_special.h"
}
FileDropWidget::FileDropWidget(QWidget *parent) :
QWidget(parent)
@ -67,3 +78,101 @@ void FileDropWidget::dropEvent(QDropEvent *event)
}
}
void MainWindow::onFileDropWidgetContextMenuRequested(const QPoint &pos)
{
QScopedPointer<QMenu> menu;
QScopedPointer<QAction> addEntryAction;
QScopedPointer<QAction> addFilesAction;
QScopedPointer<QAction> addFolderAction;
QScopedPointer<QAction> editAction;
QScopedPointer<QAction> deleteAction;
QPointer<QAction> selectedAction;
QPoint cursorPos = QCursor::pos();
QHash<QString, QString> contentHash = getCurrentContentHash();
menu.reset(new QMenu(this));
addEntryAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADD_ENTRY)), this));
addFilesAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADD_FILES)), this));
addFolderAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADD_FOLDER)), this));
editAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_EDIT)), this));
deleteAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DELETE)), this));
menu->addAction(addEntryAction.data());
menu->addAction(addFilesAction.data());
menu->addAction(addFolderAction.data());
if (!contentHash.isEmpty())
{
menu->addAction(editAction.data());
menu->addAction(deleteAction.data());
}
selectedAction = menu->exec(cursorPos);
if (!selectedAction)
return;
if (selectedAction == addFilesAction.data())
{
QStringList filePaths = QFileDialog::getOpenFileNames(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_FILES));
if (!filePaths.isEmpty())
addFilesToPlaylist(filePaths);
}
else if (selectedAction == addEntryAction.data())
{
addFilesToPlaylist(QStringList());
}
else if (selectedAction == addFolderAction.data())
{
QString dirPath = QFileDialog::getExistingDirectory(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_FOLDER), QString(), QFileDialog::ShowDirsOnly);
if (!dirPath.isEmpty())
addFilesToPlaylist(QStringList() << dirPath);
}
else if (selectedAction == editAction.data())
{
PlaylistEntryDialog *playlistDialog = playlistEntryDialog();
QHash<QString, QString> selectedCore;
QString selectedDatabase;
QString selectedName;
QString selectedPath;
QString currentPlaylistPath = getCurrentPlaylistPath();
if (!playlistDialog->showDialog(contentHash))
return;
selectedName = m_playlistEntryDialog->getSelectedName();
selectedPath = m_playlistEntryDialog->getSelectedPath();
selectedCore = m_playlistEntryDialog->getSelectedCore();
selectedDatabase = m_playlistEntryDialog->getSelectedDatabase();
if (selectedCore.isEmpty())
{
selectedCore["core_name"] = "DETECT";
selectedCore["core_path"] = "DETECT";
}
if (selectedDatabase.isEmpty())
{
selectedDatabase = QFileInfo(currentPlaylistPath).fileName().remove(file_path_str(FILE_PATH_LPL_EXTENSION));
}
contentHash["label"] = selectedName;
contentHash["path"] = selectedPath;
contentHash["core_name"] = selectedCore.value("core_name");
contentHash["core_path"] = selectedCore.value("core_path");
contentHash["db_name"] = selectedDatabase;
if (!updateCurrentPlaylistEntry(contentHash))
{
showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_UPDATE_PLAYLIST_ENTRY), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
return;
}
}
else if (selectedAction == deleteAction.data())
{
deleteCurrentPlaylistItem();
}
}

View File

@ -559,7 +559,7 @@ void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&)
strlcpy(settings->arrays.playlist_cores,
new_playlist_cores, sizeof(settings->arrays.playlist_cores));
}
else if (selectedAction == deletePlaylistAction.data())
else if (selectedItem && selectedAction == deletePlaylistAction.data())
{
if (currentPlaylistFile.exists())
{
@ -583,7 +583,7 @@ void MainWindow::onPlaylistWidgetContextMenuRequested(const QPoint&)
reloadPlaylists();
}
else if (selectedAction == hideAction.data())
else if (selectedItem && selectedAction == hideAction.data())
{
int row = m_listWidget->row(selectedItem);

View File

@ -1,10 +1,46 @@
#include <QCloseEvent>
#include <QResizeEvent>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QGroupBox>
#include <QFileInfo>
#include <QFormLayout>
#include <QSpinBox>
#include <QDoubleSpinBox>
#include <QCheckBox>
#include <QComboBox>
#include "shaderparamsdialog.h"
#include "../ui_qt.h"
extern "C" {
#include "../../../command.h"
#ifdef HAVE_MENU
#include "../../../menu/menu_shader.h"
#endif
}
ShaderParamsDialog::ShaderParamsDialog(QWidget *parent) :
QDialog(parent)
,m_layout(NULL)
{
QScrollArea *scrollArea = NULL;
QWidget *widget = NULL;
setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS));
setObjectName("shaderParamsDialog");
m_layout = new QVBoxLayout();
widget = new QWidget();
widget->setLayout(m_layout);
widget->setObjectName("shaderParamsWidget");
scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
scrollArea->setWidget(widget);
scrollArea->setObjectName("shaderParamsScrollArea");
setProperty("scrollArea", QVariant::fromValue(scrollArea));
}
ShaderParamsDialog::~ShaderParamsDialog()
@ -13,9 +49,20 @@ ShaderParamsDialog::~ShaderParamsDialog()
void ShaderParamsDialog::resizeEvent(QResizeEvent *event)
{
QVariant scrollAreaVariant = property("scrollArea");
QScrollArea *scrollArea = NULL;
QDialog::resizeEvent(event);
emit resized(event->size());
if (!scrollAreaVariant.isValid())
return;
scrollArea = scrollAreaVariant.value<QScrollArea*>();
if (!scrollArea)
return;
scrollArea->resize(event->size());
}
void ShaderParamsDialog::closeEvent(QCloseEvent *event)
@ -25,3 +72,590 @@ void ShaderParamsDialog::closeEvent(QCloseEvent *event)
emit closed();
}
QString ShaderParamsDialog::getFilterLabel(unsigned filter)
{
QString filterString;
switch (filter)
{
case 0:
filterString = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE);
break;
case 1:
filterString = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_LINEAR);
break;
case 2:
filterString = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NEAREST);
break;
default:
break;
}
return filterString;
}
void ShaderParamsDialog::clearLayout(QLayout *layout)
{
QLayoutItem *child = NULL;
while (layout->count() && ((child = layout->takeAt(0)) != 0))
{
QWidget *widget = child->widget();
QLayout *childLayout = child->layout();
if (widget)
{
QLayout *widgetLayout = widget->layout();
if (widgetLayout)
clearLayout(widgetLayout);
/* deleteLater() doesn't work right for some reason here,
* so just disconnect any signals in case there are pending events,
* and delete the widget immediately.
*/
widget->disconnect();
delete widget;
}
if (childLayout)
clearLayout(childLayout);
delete child;
}
}
void ShaderParamsDialog::getShaders(struct video_shader **menu_shader, struct video_shader **video_shader)
{
video_shader_ctx_t shader_info = {0};
#ifdef HAVE_MENU
struct video_shader *shader = menu_shader_get();
if (menu_shader)
{
if (shader)
{
*menu_shader = shader;
}
else
{
*menu_shader = NULL;
}
}
if (video_shader)
{
if (shader)
{
*video_shader = shader_info.data;
}
else
{
*video_shader = NULL;
}
}
#endif
if (video_shader)
{
if (!video_shader_driver_get_current_shader(&shader_info))
{
*video_shader = NULL;
return;
}
if (!shader_info.data || shader_info.data->num_parameters > GFX_MAX_PARAMETERS)
{
*video_shader = NULL;
return;
}
if (shader_info.data)
{
*video_shader = shader_info.data;
}
else
{
*video_shader = NULL;
}
}
}
void ShaderParamsDialog::onFilterComboBoxIndexChanged(int)
{
QComboBox *comboBox = qobject_cast<QComboBox*>(sender());
QVariant passVariant;
int pass = 0;
bool ok = false;
struct video_shader *menu_shader = NULL;
struct video_shader *video_shader = NULL;
getShaders(&menu_shader, &video_shader);
if (!comboBox)
return;
passVariant = comboBox->property("pass");
if (!passVariant.isValid())
return;
pass = passVariant.toInt(&ok);
if (!ok)
return;
if (menu_shader && pass >= 0 && pass < static_cast<int>(menu_shader->passes))
{
QVariant data = comboBox->currentData();
if (data.isValid())
{
unsigned filter = data.toUInt(&ok);
if (ok)
{
if (menu_shader)
menu_shader->pass[pass].filter = filter;
if (video_shader)
video_shader->pass[pass].filter = filter;
command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
}
}
}
}
void ShaderParamsDialog::onScaleComboBoxIndexChanged(int)
{
QComboBox *comboBox = qobject_cast<QComboBox*>(sender());
QVariant passVariant;
int pass = 0;
bool ok = false;
struct video_shader *menu_shader = NULL;
struct video_shader *video_shader = NULL;
getShaders(&menu_shader, &video_shader);
if (!comboBox)
return;
passVariant = comboBox->property("pass");
if (!passVariant.isValid())
return;
pass = passVariant.toInt(&ok);
if (!ok)
return;
if (menu_shader && pass >= 0 && pass < static_cast<int>(menu_shader->passes))
{
QVariant data = comboBox->currentData();
if (data.isValid())
{
unsigned scale = data.toUInt(&ok);
if (ok)
{
if (menu_shader)
{
menu_shader->pass[pass].fbo.scale_x = scale;
menu_shader->pass[pass].fbo.scale_y = scale;
menu_shader->pass[pass].fbo.valid = scale;
}
if (video_shader)
{
video_shader->pass[pass].fbo.scale_x = scale;
video_shader->pass[pass].fbo.scale_y = scale;
video_shader->pass[pass].fbo.valid = scale;
}
command_event(CMD_EVENT_SHADERS_APPLY_CHANGES, NULL);
}
}
}
}
void ShaderParamsDialog::reload()
{
struct video_shader *menu_shader = NULL;
struct video_shader *video_shader = NULL;
int i;
unsigned j;
getShaders(&menu_shader, &video_shader);
/* NOTE: For some reason, menu_shader_get() returns a COPY of what get_current_shader() gives us.
* And if you want to be able to change shader settings/parameters from both the raster menu and
* Qt at the same time... you must change BOTH or one will overwrite the other.
*/
if ((video_shader && video_shader->passes == 0) || !video_shader)
goto end;
clearLayout(m_layout);
/* NOTE: We assume that parameters are always grouped in order by the pass number, e.g., all parameters for pass 0 come first, then params for pass 1, etc. */
for (i = 0; i < static_cast<int>(video_shader->passes); i++)
{
QFormLayout *form = NULL;
QGroupBox *groupBox = NULL;
QFileInfo fileInfo(video_shader->pass[i].source.path);
QString shaderBasename = fileInfo.completeBaseName();
QHBoxLayout *filterScaleHBoxLayout = NULL;
QComboBox *filterComboBox = new QComboBox();
QComboBox *scaleComboBox = new QComboBox();
unsigned j = 0;
filterComboBox->setProperty("pass", i);
scaleComboBox->setProperty("pass", i);
for (;;)
{
QString filterLabel = getFilterLabel(j);
if (filterLabel.isEmpty())
break;
if (j == 0)
filterLabel = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE);
filterComboBox->addItem(filterLabel, j);
j++;
}
for (j = 0; j < 7; j++)
{
QString label;
if (j == 0)
label = msg_hash_to_str(MENU_ENUM_LABEL_VALUE_DONT_CARE);
else
label = QString::number(j) + "x";
scaleComboBox->addItem(label, j);
}
filterComboBox->setCurrentIndex(static_cast<int>(video_shader->pass[i].filter));
scaleComboBox->setCurrentIndex(static_cast<int>(video_shader->pass[i].fbo.scale_x));
/* connect the signals only after the initial index is set */
connect(filterComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onFilterComboBoxIndexChanged(int)));
connect(scaleComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(onScaleComboBoxIndexChanged(int)));
form = new QFormLayout();
groupBox = new QGroupBox(shaderBasename);
groupBox->setLayout(form);
m_layout->addWidget(groupBox);
filterScaleHBoxLayout = new QHBoxLayout();
filterScaleHBoxLayout->addWidget(new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_FILTER)));
filterScaleHBoxLayout->addWidget(filterComboBox);
filterScaleHBoxLayout->addWidget(new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SCALE)));
filterScaleHBoxLayout->addWidget(scaleComboBox);
form->addRow("", filterScaleHBoxLayout);
for (j = 0; j < video_shader->num_parameters; j++)
{
struct video_shader_parameter *param = &video_shader->parameters[j];
if (param->pass != i)
continue;
addShaderParam(param, j, form);
}
}
m_layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
end:
resize(720, 480);
show();
}
void ShaderParamsDialog::addShaderParam(struct video_shader_parameter *param, int parameter, QFormLayout *form)
{
QString desc = param->desc;
if ((param->minimum == 0.0)
&& (param->maximum
== (param->minimum
+ param->step)))
{
/* option is basically a bool, so use a checkbox */
QCheckBox *checkBox = new QCheckBox(this);
checkBox->setChecked(param->current == param->maximum ? true : false);
checkBox->setProperty("pass", param->pass);
connect(checkBox, SIGNAL(clicked()), this, SLOT(onShaderParamCheckBoxClicked()));
form->addRow(desc, checkBox);
}
else
{
QDoubleSpinBox *doubleSpinBox = NULL;
QSpinBox *spinBox = NULL;
QHBoxLayout *box = new QHBoxLayout();
QSlider *slider = new QSlider(Qt::Horizontal, this);
double value = MainWindow::lerp(param->minimum, param->maximum, 0, 100, param->current);
double intpart = 0;
bool stepIsFractional = modf(param->step, &intpart);
slider->setRange(0, 100);
slider->setSingleStep(1);
slider->setValue(value);
slider->setProperty("param", parameter);
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(onShaderParamSliderValueChanged(int)));
box->addWidget(slider);
if (stepIsFractional)
{
doubleSpinBox = new QDoubleSpinBox(this);
doubleSpinBox->setRange(param->minimum, param->maximum);
doubleSpinBox->setSingleStep(param->step);
doubleSpinBox->setValue(param->current);
doubleSpinBox->setProperty("slider", QVariant::fromValue(slider));
slider->setProperty("doubleSpinBox", QVariant::fromValue(doubleSpinBox));
connect(doubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onShaderParamDoubleSpinBoxValueChanged(double)));
box->addWidget(doubleSpinBox);
}
else
{
spinBox = new QSpinBox(this);
spinBox->setRange(param->minimum, param->maximum);
spinBox->setSingleStep(param->step);
spinBox->setValue(param->current);
spinBox->setProperty("slider", QVariant::fromValue(slider));
slider->setProperty("spinBox", QVariant::fromValue(spinBox));
connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(onShaderParamSpinBoxValueChanged(int)));
box->addWidget(spinBox);
}
form->addRow(desc, box);
}
}
void ShaderParamsDialog::onShaderParamCheckBoxClicked()
{
QCheckBox *checkBox = qobject_cast<QCheckBox*>(sender());
QVariant paramVariant;
struct video_shader *menu_shader = NULL;
struct video_shader *video_shader = NULL;
getShaders(&menu_shader, &video_shader);
if (!checkBox)
return;
if (menu_shader && menu_shader->passes == 0)
return;
paramVariant = checkBox->property("parameter");
if (paramVariant.isValid())
{
bool ok = false;
int parameter = paramVariant.toInt(&ok);
if (!ok)
return;
if (menu_shader)
{
struct video_shader_parameter *param = NULL;
param = &menu_shader->parameters[parameter];
if (param)
param->current = (checkBox->isChecked() ? param->maximum : param->minimum);
}
if (video_shader)
{
struct video_shader_parameter *param = NULL;
param = &video_shader->parameters[parameter];
if (param)
param->current = (checkBox->isChecked() ? param->maximum : param->minimum);
}
}
}
void ShaderParamsDialog::onShaderParamSliderValueChanged(int)
{
QVariant spinBoxVariant;
QVariant paramVariant;
QSlider *slider = qobject_cast<QSlider*>(sender());
struct video_shader *menu_shader = NULL;
struct video_shader *video_shader = NULL;
double newValue = 0.0;
getShaders(&menu_shader, &video_shader);
if (!slider)
return;
spinBoxVariant = slider->property("spinBox");
paramVariant = slider->property("param");
if (paramVariant.isValid())
{
bool ok = false;
int parameter = paramVariant.toInt(&ok);
if (ok)
{
if (menu_shader)
{
struct video_shader_parameter *param = &menu_shader->parameters[parameter];
newValue = MainWindow::lerp(0, 100, param->minimum, param->maximum, slider->value());
param->current = newValue;
}
if (video_shader)
{
struct video_shader_parameter *param = &video_shader->parameters[parameter];
newValue = MainWindow::lerp(0, 100, param->minimum, param->maximum, slider->value());
param->current = newValue;
}
}
}
if (spinBoxVariant.isValid())
{
QSpinBox *spinBox = spinBoxVariant.value<QSpinBox*>();
if (!spinBox)
return;
spinBox->blockSignals(true);
spinBox->setValue(newValue);
spinBox->blockSignals(false);
}
else
{
QVariant doubleSpinBoxVariant = slider->property("doubleSpinBox");
QDoubleSpinBox *doubleSpinBox = doubleSpinBoxVariant.value<QDoubleSpinBox*>();
if (!doubleSpinBox)
return;
doubleSpinBox->blockSignals(true);
doubleSpinBox->setValue(newValue);
doubleSpinBox->blockSignals(false);
}
}
void ShaderParamsDialog::onShaderParamSpinBoxValueChanged(int value)
{
QSpinBox *spinBox = qobject_cast<QSpinBox*>(sender());
QVariant sliderVariant;
QVariant paramVariant;
QSlider *slider = NULL;
struct video_shader *menu_shader = NULL;
struct video_shader *video_shader = NULL;
double newValue = 0.0;
getShaders(&menu_shader, &video_shader);
if (!spinBox)
return;
sliderVariant = spinBox->property("slider");
if (!sliderVariant.isValid())
return;
slider = sliderVariant.value<QSlider*>();
if (!slider)
return;
paramVariant = slider->property("param");
if (paramVariant.isValid())
{
bool ok = false;
int parameter = paramVariant.toInt(&ok);
if (ok)
{
if (menu_shader)
{
struct video_shader_parameter *param = &menu_shader->parameters[parameter];
param->current = value;
newValue = MainWindow::lerp(param->minimum, param->maximum, 0, 100, param->current);
slider->blockSignals(true);
slider->setValue(newValue);
slider->blockSignals(false);
}
if (video_shader)
{
struct video_shader_parameter *param = &video_shader->parameters[parameter];
param->current = value;
newValue = MainWindow::lerp(param->minimum, param->maximum, 0, 100, param->current);
slider->blockSignals(true);
slider->setValue(newValue);
slider->blockSignals(false);
}
}
}
}
void ShaderParamsDialog::onShaderParamDoubleSpinBoxValueChanged(double value)
{
QDoubleSpinBox *doubleSpinBox = qobject_cast<QDoubleSpinBox*>(sender());
QVariant sliderVariant;
QVariant paramVariant;
QSlider *slider = NULL;
struct video_shader_parameter *param = NULL;
double newValue = 0.0;
if (!doubleSpinBox)
return;
sliderVariant = doubleSpinBox->property("slider");
if (!sliderVariant.isValid())
return;
slider = sliderVariant.value<QSlider*>();
if (!slider)
return;
paramVariant = slider->property("param");
if (paramVariant.isValid())
{
param = paramVariant.value<struct video_shader_parameter*>();
if (param)
{
param->current = value;
newValue = MainWindow::lerp(param->minimum, param->maximum, 0, 100, param->current);
slider->blockSignals(true);
slider->setValue(newValue);
slider->blockSignals(false);
}
}
}

View File

@ -5,6 +5,9 @@
class QCloseEvent;
class QResizeEvent;
class QVBoxLayout;
class QFormLayout;
class QLayout;
class ShaderParamsDialog : public QDialog
{
@ -15,6 +18,22 @@ public:
signals:
void closed();
void resized(QSize size);
public slots:
void reload();
private slots:
void onShaderParamCheckBoxClicked();
void onShaderParamSliderValueChanged(int value);
void onShaderParamSpinBoxValueChanged(int value);
void onShaderParamDoubleSpinBoxValueChanged(double value);
void onFilterComboBoxIndexChanged(int index);
void onScaleComboBoxIndexChanged(int index);
private:
QString getFilterLabel(unsigned filter);
void addShaderParam(struct video_shader_parameter *param, int parameter, QFormLayout *form);
void clearLayout(QLayout *layout);
void getShaders(struct video_shader **menu_shader, struct video_shader **video_shader);
QVBoxLayout *m_layout;
protected:
void closeEvent(QCloseEvent *event);
void resizeEvent(QResizeEvent *event);

View File

@ -314,7 +314,7 @@ MainWindow::MainWindow(QWidget *parent) :
,m_allPlaylistsGridMaxCount(0)
,m_playlistEntryDialog(NULL)
,m_statusMessageElapsedTimer()
,m_shaderParamsDialog()
,m_shaderParamsDialog(new ShaderParamsDialog())
,m_networkManager(new QNetworkAccessManager(this))
,m_updateProgressDialog(new QProgressDialog())
,m_updateFile()
@ -585,312 +585,6 @@ double MainWindow::lerp(double x, double y, double a, double b, double d) {
return a + (b - a) * ((double)(d - x) / (double)(y - x));
}
void MainWindow::onShaderParamsDialogResized(QSize size)
{
QVariant scrollAreaVariant = m_shaderParamsDialog->property("scrollArea");
QScrollArea *scrollArea = NULL;
if (!scrollAreaVariant.isValid())
return;
scrollArea = scrollAreaVariant.value<QScrollArea*>();
if (!scrollArea)
return;
scrollArea->resize(size);
}
void MainWindow::onShaderParamsClicked()
{
video_shader_ctx_t shader_info = {0};
unsigned i;
int last_pass = -1;
QFormLayout *last_form = NULL;
QGroupBox *last_group = NULL;
QScrollArea *scrollArea = NULL;
QWidget *widget = NULL;
QVBoxLayout *layout = NULL;
if (!video_shader_driver_get_current_shader(&shader_info))
return;
if (!shader_info.data || shader_info.data->num_parameters > GFX_MAX_PARAMETERS)
return;
/* shader might have changed, so re-create the entire window */
if (m_shaderParamsDialog)
delete m_shaderParamsDialog;
m_shaderParamsDialog = new ShaderParamsDialog();
m_shaderParamsDialog->setWindowTitle(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_VIDEO_SHADER_PARAMETERS));
m_shaderParamsDialog->setObjectName("shaderParamsDialog");
layout = new QVBoxLayout();
widget = new QWidget();
widget->setLayout(layout);
widget->setObjectName("shaderParamsWidget");
scrollArea = new QScrollArea(m_shaderParamsDialog);
scrollArea->setWidgetResizable(true);
scrollArea->setWidget(widget);
scrollArea->setObjectName("shaderParamsScrollArea");
m_shaderParamsDialog->setProperty("scrollArea", QVariant::fromValue(scrollArea));
connect(m_shaderParamsDialog, SIGNAL(closed()), m_shaderParamsDialog, SLOT(deleteLater()));
connect(m_shaderParamsDialog, SIGNAL(resized(QSize)), this, SLOT(onShaderParamsDialogResized(QSize)));
if (shader_info.data->num_parameters == 0)
{
QLabel *label = new QLabel(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_SHADER_PARAMETERS), m_shaderParamsDialog);
label->setAlignment(Qt::AlignCenter);
layout->addWidget(label);
}
else
{
/* NOTE: We assume that parameters are always grouped in order by the pass number, e.g., all parameters for pass 0 come first, then params for pass 1, etc. */
for (i = 0; i < shader_info.data->num_parameters; i++)
{
struct video_shader_parameter *param = &shader_info.data->parameters[i];
QString desc = param->desc;
QFormLayout *form = last_form;
if (param->pass > last_pass)
{
QGroupBox *groupBox = NULL;
QFileInfo fileInfo(shader_info.data->pass[param->pass].source.path);
QString shaderBasename = fileInfo.completeBaseName();
form = new QFormLayout();
groupBox = new QGroupBox(shaderBasename);
groupBox->setLayout(form);
layout->addWidget(groupBox);
last_form = form;
last_pass = param->pass;
}
if ((param->minimum == 0.0)
&& (param->maximum
== (param->minimum
+ param->step)))
{
/* option is basically a bool, so use a checkbox */
QCheckBox *checkBox = new QCheckBox(m_shaderParamsDialog);
checkBox->setChecked(param->current == param->maximum ? true : false);
checkBox->setProperty("param", QVariant::fromValue(param));
connect(checkBox, SIGNAL(clicked()), this, SLOT(onShaderParamCheckBoxClicked()));
form->addRow(desc, checkBox);
}
else
{
QDoubleSpinBox *doubleSpinBox = NULL;
QSpinBox *spinBox = NULL;
QHBoxLayout *box = new QHBoxLayout();
QSlider *slider = new QSlider(Qt::Horizontal, m_shaderParamsDialog);
double value = lerp(param->minimum, param->maximum, 0, 100, param->current);
double intpart = 0;
bool stepIsFractional = modf(param->step, &intpart);
slider->setRange(0, 100);
slider->setSingleStep(1);
slider->setValue(value);
slider->setProperty("param", QVariant::fromValue(param));
connect(slider, SIGNAL(valueChanged(int)), this, SLOT(onShaderParamSliderValueChanged(int)));
box->addWidget(slider);
if (stepIsFractional)
{
doubleSpinBox = new QDoubleSpinBox(m_shaderParamsDialog);
doubleSpinBox->setRange(param->minimum, param->maximum);
doubleSpinBox->setSingleStep(param->step);
doubleSpinBox->setValue(param->current);
doubleSpinBox->setProperty("slider", QVariant::fromValue(slider));
slider->setProperty("doubleSpinBox", QVariant::fromValue(doubleSpinBox));
connect(doubleSpinBox, SIGNAL(valueChanged(double)), this, SLOT(onShaderParamDoubleSpinBoxValueChanged(double)));
box->addWidget(doubleSpinBox);
}
else
{
spinBox = new QSpinBox(m_shaderParamsDialog);
spinBox->setRange(param->minimum, param->maximum);
spinBox->setSingleStep(param->step);
spinBox->setValue(param->current);
spinBox->setProperty("slider", QVariant::fromValue(slider));
slider->setProperty("spinBox", QVariant::fromValue(spinBox));
connect(spinBox, SIGNAL(valueChanged(int)), this, SLOT(onShaderParamSpinBoxValueChanged(int)));
box->addWidget(spinBox);
}
form->addRow(desc, box);
}
}
layout->addItem(new QSpacerItem(20, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
}
m_shaderParamsDialog->resize(720, 480);
m_shaderParamsDialog->show();
}
void MainWindow::onShaderParamCheckBoxClicked()
{
QCheckBox *checkBox = qobject_cast<QCheckBox*>(sender());
QVariant paramVariant;
struct video_shader_parameter *param = NULL;
if (!checkBox)
return;
paramVariant = checkBox->property("param");
if (paramVariant.isValid())
{
param = paramVariant.value<struct video_shader_parameter*>();
if (param)
param->current = (checkBox->isChecked() ? param->maximum : param->minimum);
}
}
void MainWindow::onShaderParamSliderValueChanged(int value)
{
QVariant spinBoxVariant;
QVariant paramVariant;
QSlider *slider = qobject_cast<QSlider*>(sender());
struct video_shader_parameter *param = NULL;
double newValue = 0.0;
if (!slider)
return;
spinBoxVariant = slider->property("spinBox");
paramVariant = slider->property("param");
if (paramVariant.isValid())
{
param = paramVariant.value<struct video_shader_parameter*>();
if (param)
{
newValue = lerp(0, 100, param->minimum, param->maximum, slider->value());
param->current = newValue;
}
}
if (spinBoxVariant.isValid())
{
QSpinBox *spinBox = spinBoxVariant.value<QSpinBox*>();
if (!spinBox)
return;
spinBox->blockSignals(true);
spinBox->setValue(newValue);
spinBox->blockSignals(false);
}
else
{
QVariant doubleSpinBoxVariant = slider->property("doubleSpinBox");
QDoubleSpinBox *doubleSpinBox = doubleSpinBoxVariant.value<QDoubleSpinBox*>();
if (!doubleSpinBox)
return;
doubleSpinBox->blockSignals(true);
doubleSpinBox->setValue(newValue);
doubleSpinBox->blockSignals(false);
}
}
void MainWindow::onShaderParamSpinBoxValueChanged(int value)
{
QSpinBox *spinBox = qobject_cast<QSpinBox*>(sender());
QVariant sliderVariant;
QVariant paramVariant;
QSlider *slider = NULL;
struct video_shader_parameter *param = NULL;
double newValue = 0.0;
if (!spinBox)
return;
sliderVariant = spinBox->property("slider");
if (!sliderVariant.isValid())
return;
slider = sliderVariant.value<QSlider*>();
if (!slider)
return;
paramVariant = slider->property("param");
if (paramVariant.isValid())
{
param = paramVariant.value<struct video_shader_parameter*>();
if (param)
{
param->current = value;
newValue = lerp(param->minimum, param->maximum, 0, 100, param->current);
slider->blockSignals(true);
slider->setValue(newValue);
slider->blockSignals(false);
}
}
}
void MainWindow::onShaderParamDoubleSpinBoxValueChanged(double value)
{
QDoubleSpinBox *doubleSpinBox = qobject_cast<QDoubleSpinBox*>(sender());
QVariant sliderVariant;
QVariant paramVariant;
QSlider *slider = NULL;
struct video_shader_parameter *param = NULL;
double newValue = 0.0;
if (!doubleSpinBox)
return;
sliderVariant = doubleSpinBox->property("slider");
if (!sliderVariant.isValid())
return;
slider = sliderVariant.value<QSlider*>();
if (!slider)
return;
paramVariant = slider->property("param");
if (paramVariant.isValid())
{
param = paramVariant.value<struct video_shader_parameter*>();
if (param)
{
param->current = value;
newValue = lerp(param->minimum, param->maximum, 0, 100, param->current);
slider->blockSignals(true);
slider->setValue(newValue);
slider->blockSignals(false);
}
}
}
void MainWindow::onGridItemClicked(ThumbnailWidget *widget)
{
QHash<QString, QString> hash;
@ -1140,105 +834,6 @@ bool MainWindow::showMessageBox(QString msg, MessageBoxType msgType, Qt::WindowM
return true;
}
void MainWindow::onFileDropWidgetContextMenuRequested(const QPoint &pos)
{
QScopedPointer<QMenu> menu;
QScopedPointer<QAction> addEntryAction;
QScopedPointer<QAction> addFilesAction;
QScopedPointer<QAction> addFolderAction;
QScopedPointer<QAction> editAction;
QScopedPointer<QAction> deleteAction;
QPointer<QAction> selectedAction;
QPoint cursorPos = QCursor::pos();
QHash<QString, QString> contentHash = getCurrentContentHash();
menu.reset(new QMenu(this));
addEntryAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADD_ENTRY)), this));
addFilesAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADD_FILES)), this));
addFolderAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_ADD_FOLDER)), this));
editAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_EDIT)), this));
deleteAction.reset(new QAction(QString(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_DELETE)), this));
menu->addAction(addEntryAction.data());
menu->addAction(addFilesAction.data());
menu->addAction(addFolderAction.data());
if (!contentHash.isEmpty())
{
menu->addAction(editAction.data());
menu->addAction(deleteAction.data());
}
selectedAction = menu->exec(cursorPos);
if (!selectedAction)
return;
if (selectedAction == addFilesAction.data())
{
QStringList filePaths = QFileDialog::getOpenFileNames(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_FILES));
if (!filePaths.isEmpty())
addFilesToPlaylist(filePaths);
}
else if (selectedAction == addEntryAction.data())
{
addFilesToPlaylist(QStringList());
}
else if (selectedAction == addFolderAction.data())
{
QString dirPath = QFileDialog::getExistingDirectory(this, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_SELECT_FOLDER), QString(), QFileDialog::ShowDirsOnly);
if (!dirPath.isEmpty())
addFilesToPlaylist(QStringList() << dirPath);
}
else if (selectedAction == editAction.data())
{
PlaylistEntryDialog *playlistDialog = playlistEntryDialog();
QHash<QString, QString> selectedCore;
QString selectedDatabase;
QString selectedName;
QString selectedPath;
QString currentPlaylistPath = getCurrentPlaylistPath();
if (!playlistDialog->showDialog(contentHash))
return;
selectedName = m_playlistEntryDialog->getSelectedName();
selectedPath = m_playlistEntryDialog->getSelectedPath();
selectedCore = m_playlistEntryDialog->getSelectedCore();
selectedDatabase = m_playlistEntryDialog->getSelectedDatabase();
if (selectedCore.isEmpty())
{
selectedCore["core_name"] = "DETECT";
selectedCore["core_path"] = "DETECT";
}
if (selectedDatabase.isEmpty())
{
selectedDatabase = QFileInfo(currentPlaylistPath).fileName().remove(file_path_str(FILE_PATH_LPL_EXTENSION));
}
contentHash["label"] = selectedName;
contentHash["path"] = selectedPath;
contentHash["core_name"] = selectedCore.value("core_name");
contentHash["core_path"] = selectedCore.value("core_path");
contentHash["db_name"] = selectedDatabase;
if (!updateCurrentPlaylistEntry(contentHash))
{
showMessageBox(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_COULD_NOT_UPDATE_PLAYLIST_ENTRY), MainWindow::MSGBOX_TYPE_ERROR, Qt::ApplicationModal, false);
return;
}
}
else if (selectedAction == deleteAction.data())
{
deleteCurrentPlaylistItem();
}
}
void MainWindow::onFileBrowserTreeContextMenuRequested(const QPoint&)
{
#ifdef HAVE_LIBRETRODB
@ -1324,10 +919,20 @@ void MainWindow::deferReloadShaderParams()
emit gotReloadShaderParams();
}
void MainWindow::onShaderParamsClicked()
{
if (!m_shaderParamsDialog)
return;
m_shaderParamsDialog->show();
onGotReloadShaderParams();
}
void MainWindow::onGotReloadShaderParams()
{
if (m_shaderParamsDialog && m_shaderParamsDialog->isVisible())
onShaderParamsClicked();
m_shaderParamsDialog->reload();
}
void MainWindow::appendLogMessage(const QString &msg)
@ -1835,7 +1440,6 @@ void MainWindow::onTableWidgetDeletePressed()
QHash<QString, QString> MainWindow::getCurrentContentHash()
{
QTableWidgetItem *contentItem = m_tableWidget->currentItem();
QListWidgetItem *playlistItem = m_listWidget->currentItem();
QHash<QString, QString> contentHash;
ViewType viewType = getCurrentViewType();

View File

@ -324,7 +324,7 @@ static void* ui_companion_qt_init(void)
QObject::connect(viewClosedDocksMenu, SIGNAL(aboutToShow()), mainwindow, SLOT(onViewClosedDocksAboutToShow()));
viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_MENU_VIEW_SHADER_PARAMS), mainwindow, SLOT(onShaderParamsClicked()));
viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SHADER_OPTIONS), mainwindow, SLOT(onShaderParamsClicked()));
viewMenu->addSeparator();
viewMenu->addAction(msg_hash_to_str(MENU_ENUM_LABEL_VALUE_QT_VIEW_TYPE_ICONS), mainwindow, SLOT(onIconViewClicked()));
@ -634,26 +634,6 @@ static void ui_companion_qt_event_command(void *data, enum event_command cmd)
switch (cmd)
{
case CMD_EVENT_SHADERS_APPLY_CHANGES:
{
/* If shader was turned off, reload the params window manually because PRESET_LOADED won't be fired */
video_shader_ctx_t shader_info = {0};
if (!video_shader_driver_get_current_shader(&shader_info))
break;
/* Isn't there a better way to do this? */
if (shader_info.data && shader_info.data->num_parameters == 0)
{
if (shader_info.data->passes == 1 && string_is_empty(shader_info.data->pass[0].source.path))
{
RARCH_LOG("[Qt]: Clearing shader parameters.\n");
win_handle->qtWindow->deferReloadShaderParams();
}
}
/* Otherwise, PRESET_LOADED fires in more situations than APPLY_CHANGES, so use that for reloading the params window */
break;
}
case CMD_EVENT_SHADER_PRESET_LOADED:
RARCH_LOG("[Qt]: Reloading shader parameters.\n");
win_handle->qtWindow->deferReloadShaderParams();

View File

@ -368,11 +368,6 @@ private slots:
void onShowErrorMessage(QString msg);
void onShowInfoMessage(QString msg);
void onContributorsClicked();
void onShaderParamCheckBoxClicked();
void onShaderParamSliderValueChanged(int value);
void onShaderParamSpinBoxValueChanged(int value);
void onShaderParamDoubleSpinBoxValueChanged(double value);
void onShaderParamsDialogResized(QSize size);
int onExtractArchive(QString path);
private:

View File

@ -57,6 +57,7 @@ typedef struct ui_companion_win32
void *empty;
} ui_companion_win32_t;
#ifndef __WINRT__
bool win32_window_init(WNDCLASSEX *wndclass,
bool fullscreen, const char *class_name)
{
@ -79,7 +80,7 @@ bool win32_window_init(WNDCLASSEX *wndclass,
return true;
}
#endif
static bool win32_browser(
HWND owner,