Skip to content

Commit

Permalink
Add hardware acceleration preference
Browse files Browse the repository at this point in the history
  • Loading branch information
klembot committed Oct 8, 2023
1 parent e0bd0cd commit 0eb933b
Show file tree
Hide file tree
Showing 19 changed files with 436 additions and 41 deletions.
3 changes: 2 additions & 1 deletion docs/en/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@
- [If An Error Message Appears While Editing](troubleshooting/error-message.md)
- [If Twine Won't Start](troubleshooting/wont-start.md)
- [If Twine Lost Your Story](troubleshooting/lost-story.md)
- [If Your Story Is Damaged](troubleshooting/damaged-story.md)
- [If Your Story Is Damaged](troubleshooting/damaged-story.md)
- [If You See Visual Glitches in Twine](troubleshooting/visual-glitches.md)
16 changes: 16 additions & 0 deletions docs/en/src/troubleshooting/visual-glitches.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# If You See Visual Glitches in Twine

This page only applies to app Twine.

If you see visual glitches often in Twine, turning off hardware accleration may
help. You should only do this if you are seeing a problem. As the name implies,
using hardware acceleration speeds up Twine's display.

To do this, go to _Troubleshooting_ under the _Help_ menu, then choose _Disable
Hardware Acceleration_. This item will be checked when hardware acceleration is
disabled, but changing the setting will only take effect the next time you
launch Twine.

You can also disable hardware acceleration by launching Twine with the
command-line switch
<code>&#x2011;&#x2011;disableHardwareAcceleration=true</code>.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Twine",
"version": "2.7.1",
"version": "2.8.0-alpha1",
"description": "a GUI for creating nonlinear stories",
"author": "Chris Klimas <[email protected]>",
"license": "GPL-3.0",
Expand Down
5 changes: 5 additions & 0 deletions public/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,7 @@
},
"menuBar": {
"checkForUpdates": "Check for Updates...",
"disableHardwareAcceleration": "Disable Hardware Acceleration",
"edit": "Edit",
"showDevTools": "Show Debug Console",
"showStoryLibrary": "Show Story Library",
Expand All @@ -270,6 +271,10 @@
"twineHelp": "Twine Help",
"view": "View"
},
"relaunchDialog": {
"defaultPrompt": "This change will take effect the next time Twine is launched.",
"relaunchNow": "Relaunch Now"
},
"scratchDirectoryName": "Scratch",
"storiesDirectoryName": "Stories",
"updateCheck": {
Expand Down
1 change: 1 addition & 0 deletions src/__mocks__/electron.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export const app = {
disableHardwareAcceleration: jest.fn(),
getName() {
return `mock-electron-app-name`;
},
Expand Down
1 change: 1 addition & 0 deletions src/__mocks__/fs-extra.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const move = jest.fn();
export const readFile = jest.fn().mockResolvedValue('');
export const readdir = jest.fn().mockResolvedValue([]);
export const readJson = jest.fn();
export const readJsonSync = jest.fn();
export const remove = jest.fn();
export const rename = jest.fn();
export const stat = jest.fn();
Expand Down
42 changes: 22 additions & 20 deletions src/electron/main-process/__tests__/app-prefs.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import minimist from 'minimist';
import {getAppPref, loadAppPrefs, setAppPref} from '../app-prefs';
import {loadJsonFile, saveJsonFile} from '../json-file';
import {loadJsonFileSync, saveJsonFile} from '../json-file';

jest.mock('minimist');
jest.mock('../json-file');
Expand All @@ -10,14 +10,14 @@ beforeEach(() => {
jest.spyOn(console, 'warn').mockReturnValue();
});

const loadJsonFileMock = loadJsonFile as jest.Mock;
const loadJsonFileSyncMock = loadJsonFileSync as jest.Mock;
const saveJsonFileMock = saveJsonFile as jest.Mock;
const minimistMock = minimist as jest.Mock;

function mockJsonFile(value: any) {
loadJsonFileMock.mockImplementation((name: string) => {
loadJsonFileSyncMock.mockImplementation((name: string) => {
if (name === 'app-prefs.json') {
return Promise.resolve(value);
return value;
}

throw new Error(`Loaded incorrect file "${name}"`);
Expand All @@ -30,47 +30,49 @@ describe('loadAppPrefs and getAppPrefs', () => {
minimistMock.mockReturnValue({});
});

it('loads prefs from command line arguments', async () => {
it('loads prefs from command line arguments', () => {
minimistMock.mockReturnValue({
scratchFolderPath: 'mock-scratch-folder-path'
});
await loadAppPrefs();
loadAppPrefs();
expect(getAppPref('scratchFolderPath')).toBe('mock-scratch-folder-path');
});

it('loads prefs from the app prefs file', async () => {
it('loads prefs from the app prefs file', () => {
mockJsonFile({scratchFolderPath: 'mock-scratch-folder-path'});
await loadAppPrefs();
loadAppPrefs();
expect(getAppPref('scratchFolderPath')).toBe('mock-scratch-folder-path');
});

it('prefers command line arguments to values set in the app prefs file', async () => {
it('prefers command line arguments to values set in the app prefs file', () => {
mockJsonFile({scratchFolderPath: 'json-path'});
minimistMock.mockReturnValue({
scratchFolderPath: 'args-path'
});
await loadAppPrefs();
loadAppPrefs();
expect(getAppPref('scratchFolderPath')).toBe('args-path');
});

it("ignores values in the app prefs file that aren't known prefs", async () => {
it("ignores values in the app prefs file that aren't known prefs", () => {
mockJsonFile({anUnrecognizedKey: 'fail'});
await loadAppPrefs();
loadAppPrefs();
expect(getAppPref('anUnrecognizedKey' as any)).toBeUndefined();
});

it("ignores values in command line arguments that aren't known prefs", async () => {
it("ignores values in command line arguments that aren't known prefs", () => {
minimistMock.mockReturnValue({anUnrecognizedKey: 'fail'});
await loadAppPrefs();
loadAppPrefs();
expect(getAppPref('anUnrecognizedKey' as any)).toBeUndefined();
});

it("doesn't throw an error if the app prefs file couldn't be loaded", async () => {
it("doesn't throw an error if the app prefs file couldn't be loaded", () => {
minimistMock.mockReturnValue({
scratchFolderPath: 'mock-scratch-folder-path'
});
loadJsonFileMock.mockRejectedValue(new Error());
await loadAppPrefs();
loadJsonFileSyncMock.mockImplementation(() => {
throw new Error();
});
loadAppPrefs();
expect(getAppPref('scratchFolderPath')).toBe('mock-scratch-folder-path');
});
});
Expand All @@ -87,13 +89,13 @@ describe('setAppPref', () => {
});

it('resolves after setting a pref', async () => {
await loadAppPrefs();
loadAppPrefs();
await setAppPref('scratchFolderPath', 'mock-change');
expect(getAppPref('scratchFolderPath')).toBe('mock-change');
});

it('resolves after saving changes to the app prefs file', async () => {
await loadAppPrefs();
loadAppPrefs();
expect(saveJsonFileMock).not.toBeCalled();
await setAppPref('scratchFolderPath', 'mock-change');
expect(saveJsonFileMock.mock.calls).toEqual([
Expand All @@ -103,7 +105,7 @@ describe('setAppPref', () => {

it('rejects if saving changes fails', async () => {
saveJsonFileMock.mockRejectedValue(new Error());
await loadAppPrefs();
loadAppPrefs();
await expect(() =>
setAppPref('scratchFolderPath', 'mock-value')
).rejects.toBeInstanceOf(Error);
Expand Down
88 changes: 88 additions & 0 deletions src/electron/main-process/__tests__/hardware-acceleration.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import {app} from 'electron';
import {AppPrefName, getAppPref, setAppPref} from '../app-prefs';
import {
initHardwareAcceleration,
toggleHardwareAcceleration
} from '../hardware-acceleration';
import {showRelaunchDialog} from '../relaunch-dialog';

jest.mock('electron');
jest.mock('../app-prefs');
jest.mock('../relaunch-dialog');

describe('initHardwareAcceleration', () => {
const getAppPrefMock = getAppPref as jest.Mock;
const disableHardwareAccelerationMock =
app.disableHardwareAcceleration as jest.Mock;

beforeEach(() => {
jest.spyOn(console, 'log').mockReturnValue();
});

it('disables hardware acceleration if the app pref is true', () => {
getAppPrefMock.mockImplementation((name: AppPrefName) => {
if (name === 'disableHardwareAcceleration') {
return true;
}

throw new Error(`Asked for a non-mocked pref: ${name}`);
});

initHardwareAcceleration();
expect(disableHardwareAccelerationMock).toBeCalledTimes(1);
});

it("doesn't disable hardware acceleration if the app pref is falsy", () => {
getAppPrefMock.mockImplementation((name: AppPrefName) => {
if (name === 'disableHardwareAcceleration') {
return undefined;
}

throw new Error(`Asked for a non-mocked pref: ${name}`);
});

initHardwareAcceleration();
expect(disableHardwareAccelerationMock).not.toBeCalled();
});
});

describe('toggleHardwareAcceleration', () => {
const getAppPrefMock = getAppPref as jest.Mock;
const setAppPrefMock = setAppPref as jest.Mock;
const showRelaunchDialogMock = showRelaunchDialog as jest.Mock;

it('sets the preference to true if the preference was falsy', () => {
getAppPrefMock.mockImplementation((name: AppPrefName) => {
if (name === 'disableHardwareAcceleration') {
return true;
}

throw new Error(`Asked for a non-mocked pref: ${name}`);
});

toggleHardwareAcceleration();
expect(setAppPrefMock.mock.calls).toEqual([
['disableHardwareAcceleration', false]
]);
});

it('sets the preference to false if the preference was true', () => {
getAppPrefMock.mockImplementation((name: AppPrefName) => {
if (name === 'disableHardwareAcceleration') {
return false;
}

throw new Error(`Asked for a non-mocked pref: ${name}`);
});

toggleHardwareAcceleration();
expect(setAppPrefMock.mock.calls).toEqual([
['disableHardwareAcceleration', true]
]);
});

it('shows the relaunch dialog', () => {
toggleHardwareAcceleration();
expect(showRelaunchDialogMock).toBeCalledTimes(1);
});
});
6 changes: 0 additions & 6 deletions src/electron/main-process/__tests__/init-app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ describe('initApp', () => {
const backupStoryDirectoryMock = backupStoryDirectory as jest.Mock;
const cleanScratchDirectoryMock = cleanScratchDirectory as jest.Mock;
const createStoryDirectoryMock = createStoryDirectory as jest.Mock;
const loadAppPrefsMock = loadAppPrefs as jest.Mock;
const onMock = app.on as jest.Mock;
const quitMock = app.quit as jest.Mock;
const showErrorBoxMock = dialog.showErrorBox as jest.Mock;
Expand Down Expand Up @@ -67,11 +66,6 @@ describe('initApp', () => {
expect(initIpcMock).toBeCalledTimes(1);
});

it('loads app prefs', async () => {
await initApp();
expect(loadAppPrefsMock).toBeCalledTimes(1);
});

it('initializes the menu bar', async () => {
await initApp();
expect(initMenuBarMock).toBeCalledTimes(1);
Expand Down
27 changes: 25 additions & 2 deletions src/electron/main-process/__tests__/json-file.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {loadJsonFile, saveJsonFile} from '../json-file';
import {readJson, writeJson} from 'fs-extra';
import {loadJsonFile, loadJsonFileSync, saveJsonFile} from '../json-file';
import {readJson, readJsonSync, writeJson} from 'fs-extra';

jest.mock('fs-extra');

Expand All @@ -24,6 +24,29 @@ describe('loadJsonFile()', () => {
});
});

describe('loadJsonFileSync()', () => {
const readJsonSyncMock = readJsonSync as jest.Mock;

it("returns the contents of a JSON file in the app's user data path", async () => {
const mockData = {test: true};

readJsonSyncMock.mockReturnValue(mockData);
expect(loadJsonFileSync('test.json')).toBe(mockData);
expect(readJsonSyncMock.mock.calls).toEqual([
['mock-electron-app-path-userData/test.json']
]);
});

it('throws an error if there is an error reading the file', async () => {
const mockError = new Error();

readJsonSyncMock.mockImplementation(() => {
throw mockError;
});
expect(() => loadJsonFileSync('test.json')).toThrow(mockError);
});
});

describe('saveJsonFile()', () => {
const writeJsonMock = writeJson as jest.Mock;

Expand Down
Loading

0 comments on commit 0eb933b

Please sign in to comment.