| +workspace.xml | ||
| -<?xml version="1.0" encoding="UTF-8"?> | ||
| -<project version="4"> | ||
| - <component name="ChangeListManager"> | ||
| - <list default="true" id="be1819c5-28d3-4984-9634-0b266734e13c" name="Default Changelist" comment=""> | ||
| - <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> | ||
| - <change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/EventFrame.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/EventFrame.java" afterDir="false" /> | ||
| - <change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/HardwareState.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/HardwareState.java" afterDir="false" /> | ||
| - <change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/HardwareSwitches.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/HardwareSwitches.java" afterDir="false" /> | ||
| - <change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/KmCaster.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/KmCaster.java" afterDir="false" /> | ||
| - <change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/SwitchName.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/SwitchName.java" afterDir="false" /> | ||
| - <change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/FrameDragListener.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/FrameDragListener.java" afterDir="false" /> | ||
| - <change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/KeyboardListener.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/KeyboardListener.java" afterDir="false" /> | ||
| - <change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/KeyboardModifier.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/KeyboardModifier.java" afterDir="false" /> | ||
| - <change beforePath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/MouseListener.java" beforeDir="false" afterPath="$PROJECT_DIR$/src/main/com/whitemagicsoftware/kmcaster/listeners/MouseListener.java" afterDir="false" /> | ||
| - </list> | ||
| - <option name="SHOW_DIALOG" value="false" /> | ||
| - <option name="HIGHLIGHT_CONFLICTS" value="true" /> | ||
| - <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" /> | ||
| - <option name="LAST_RESOLUTION" value="IGNORE" /> | ||
| - </component> | ||
| - <component name="ExternalProjectsData"> | ||
| - <projectState path="$PROJECT_DIR$"> | ||
| - <ProjectState /> | ||
| - </projectState> | ||
| - </component> | ||
| - <component name="ExternalProjectsManager"> | ||
| - <system id="GRADLE"> | ||
| - <state> | ||
| - <task path="$PROJECT_DIR$"> | ||
| - <activation /> | ||
| - </task> | ||
| - <projects_view> | ||
| - <tree_state> | ||
| - <expand> | ||
| - <path> | ||
| - <item name="" type="6a2764b6:ExternalProjectsStructure$RootNode" /> | ||
| - <item name="kmcaster" type="f1a62948:ProjectNode" /> | ||
| - </path> | ||
| - </expand> | ||
| - <select /> | ||
| - </tree_state> | ||
| - </projects_view> | ||
| - </state> | ||
| - </system> | ||
| - </component> | ||
| - <component name="FileTemplateManagerImpl"> | ||
| - <option name="RECENT_TEMPLATES"> | ||
| - <list> | ||
| - <option value="Enum" /> | ||
| - <option value="Class" /> | ||
| - </list> | ||
| - </option> | ||
| - </component> | ||
| - <component name="Git.Settings"> | ||
| - <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" /> | ||
| - </component> | ||
| - <component name="ProjectId" id="1ekBeNU4c094edVfyTkiboUOdYP" /> | ||
| - <component name="ProjectLevelVcsManager"> | ||
| - <ConfirmationsSetting value="2" id="Add" /> | ||
| - </component> | ||
| - <component name="ProjectViewState"> | ||
| - <option name="hideEmptyMiddlePackages" value="true" /> | ||
| - <option name="showLibraryContents" value="true" /> | ||
| - </component> | ||
| - <component name="PropertiesComponent"> | ||
| - <property name="ASKED_ADD_EXTERNAL_FILES" value="true" /> | ||
| - <property name="RunOnceActivity.OpenProjectViewOnStart" value="true" /> | ||
| - <property name="RunOnceActivity.ShowReadmeOnStart" value="true" /> | ||
| - <property name="last_opened_file_path" value="$PROJECT_DIR$" /> | ||
| - <property name="project.structure.last.edited" value="Modules" /> | ||
| - <property name="project.structure.proportion" value="0.15" /> | ||
| - <property name="project.structure.side.proportion" value="0.21724138" /> | ||
| - <property name="settings.editor.selected.configurable" value="File.Encoding" /> | ||
| - <property name="show.unlinked.gradle.project.popup" value="true" /> | ||
| - </component> | ||
| - <component name="RecentsManager"> | ||
| - <key name="MoveInnerDialog.RECENTS_KEY"> | ||
| - <recent name="com.whitemagicsoftware.kmcaster" /> | ||
| - <recent name="com.whitemagicsoftware.kmcaster.listeners" /> | ||
| - </key> | ||
| - <key name="CreateClassDialog.RecentsKey"> | ||
| - <recent name="com.whitemagicsoftware.kmcaster" /> | ||
| - <recent name="com.whitemagicsoftware.kmcaster.listeners" /> | ||
| - </key> | ||
| - </component> | ||
| - <component name="RunManager"> | ||
| - <configuration name="KmCaster" type="Application" factoryName="Application" temporary="true" nameIsGenerated="true"> | ||
| - <option name="MAIN_CLASS_NAME" value="com.whitemagicsoftware.kmcaster.KmCaster" /> | ||
| - <module name="kmcaster.main" /> | ||
| - <extension name="coverage"> | ||
| - <pattern> | ||
| - <option name="PATTERN" value="com.whitemagicsoftware.kmcaster.listeners.*" /> | ||
| - <option name="ENABLED" value="true" /> | ||
| - </pattern> | ||
| - </extension> | ||
| - <method v="2"> | ||
| - <option name="Make" enabled="true" /> | ||
| - </method> | ||
| - </configuration> | ||
| - <recent_temporary> | ||
| - <list> | ||
| - <item itemvalue="Application.KmCaster" /> | ||
| - </list> | ||
| - </recent_temporary> | ||
| - </component> | ||
| - <component name="SvnConfiguration"> | ||
| - <configuration /> | ||
| - </component> | ||
| - <component name="TaskManager"> | ||
| - <task active="true" id="Default" summary="Default task"> | ||
| - <changelist id="be1819c5-28d3-4984-9634-0b266734e13c" name="Default Changelist" comment="" /> | ||
| - <created>1594855804356</created> | ||
| - <option name="number" value="Default" /> | ||
| - <option name="presentableId" value="Default" /> | ||
| - <updated>1594855804356</updated> | ||
| - </task> | ||
| - <servers /> | ||
| - </component> | ||
| - <component name="VcsManagerConfiguration"> | ||
| - <option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" /> | ||
| - </component> | ||
| - <component name="WindowStateProjectService"> | ||
| - <state x="813" y="683" width="218" height="250" key="#Notifications" timestamp="1594856235998"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="813" y="683" width="218" height="250" key="#Notifications/0.28.2560.1529@0.28.2560.1529" timestamp="1594856235998" /> | ||
| - <state x="314" y="436" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1594970464272"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="314" y="436" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594970464272" /> | ||
| - <state x="734" y="519" width="477" height="578" key="#com.intellij.ide.util.MemberChooser" timestamp="1594969516839"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="734" y="519" width="477" height="578" key="#com.intellij.ide.util.MemberChooser/0.28.2560.1529@0.28.2560.1529" timestamp="1594969516839" /> | ||
| - <state x="745" y="737" key="#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog" timestamp="1594958988910"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="745" y="737" key="#com.intellij.refactoring.move.MoveHandler.SelectRefactoringDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594958988910" /> | ||
| - <state x="707" y="657" width="431" height="302" key="#com.intellij.refactoring.move.moveInner.MoveInnerDialog" timestamp="1594958994663"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="707" y="657" width="431" height="302" key="#com.intellij.refactoring.move.moveInner.MoveInnerDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594958994663" /> | ||
| - <state x="567" y="28" width="711" height="1526" key="#com.intellij.refactoring.rename.AutomaticRenamingDialog" timestamp="1594967090738"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="567" y="28" width="711" height="1526" key="#com.intellij.refactoring.rename.AutomaticRenamingDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594967090738" /> | ||
| - <state x="659" y="559" width="526" height="497" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog" timestamp="1594880960290"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="659" y="559" width="526" height="497" key="#com.intellij.refactoring.safeDelete.UnsafeUsagesDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594880960290" /> | ||
| - <state x="710" y="704" width="425" height="207" key="DetectedRootsChooserDialog" timestamp="1594872764235"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="710" y="704" width="425" height="207" key="DetectedRootsChooserDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594872764235" /> | ||
| - <state width="1479" height="330" key="GridCell.Tab.0.bottom" timestamp="1594972026417"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state width="1479" height="330" key="GridCell.Tab.0.bottom/0.28.2560.1529@0.28.2560.1529" timestamp="1594972026417" /> | ||
| - <state width="1479" height="330" key="GridCell.Tab.0.center" timestamp="1594972026417"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state width="1479" height="330" key="GridCell.Tab.0.center/0.28.2560.1529@0.28.2560.1529" timestamp="1594972026417" /> | ||
| - <state width="1479" height="330" key="GridCell.Tab.0.left" timestamp="1594972026416"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state width="1479" height="330" key="GridCell.Tab.0.left/0.28.2560.1529@0.28.2560.1529" timestamp="1594972026416" /> | ||
| - <state width="1479" height="330" key="GridCell.Tab.0.right" timestamp="1594972026417"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state width="1479" height="330" key="GridCell.Tab.0.right/0.28.2560.1529@0.28.2560.1529" timestamp="1594972026417" /> | ||
| - <state width="1479" height="406" key="GridCell.Tab.1.bottom" timestamp="1594959771008"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state width="1479" height="406" key="GridCell.Tab.1.bottom/0.28.2560.1529@0.28.2560.1529" timestamp="1594959771008" /> | ||
| - <state width="1479" height="406" key="GridCell.Tab.1.center" timestamp="1594959771007"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state width="1479" height="406" key="GridCell.Tab.1.center/0.28.2560.1529@0.28.2560.1529" timestamp="1594959771007" /> | ||
| - <state width="1479" height="406" key="GridCell.Tab.1.left" timestamp="1594959771006"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state width="1479" height="406" key="GridCell.Tab.1.left/0.28.2560.1529@0.28.2560.1529" timestamp="1594959771006" /> | ||
| - <state width="1479" height="406" key="GridCell.Tab.1.right" timestamp="1594959771007"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state width="1479" height="406" key="GridCell.Tab.1.right/0.28.2560.1529@0.28.2560.1529" timestamp="1594959771007" /> | ||
| - <state x="423" y="444" width="1026" height="842" key="SettingsEditor" timestamp="1594876039235"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="423" y="444" width="1026" height="842" key="SettingsEditor/0.28.2560.1529@0.28.2560.1529" timestamp="1594876039235" /> | ||
| - <state width="761" height="601" key="XDebugger.FullValuePopup" timestamp="1594959421070"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state width="761" height="601" key="XDebugger.FullValuePopup/0.28.2560.1529@0.28.2560.1529" timestamp="1594959421070" /> | ||
| - <state x="1068" y="347" width="1323" height="1003" key="com.intellij.history.integration.ui.views.FileHistoryDialog" timestamp="1594955523088"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="1068" y="347" width="1323" height="1003" key="com.intellij.history.integration.ui.views.FileHistoryDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594955523088" /> | ||
| - <state x="840" y="410" width="564" height="770" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser" timestamp="1594946566725"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="840" y="410" width="564" height="770" key="com.intellij.openapi.editor.actions.MultiplePasteAction$ClipboardContentChooser/0.28.2560.1529@0.28.2560.1529" timestamp="1594946566725" /> | ||
| - <state x="833" y="545" width="586" height="502" key="find.popup" timestamp="1594972340355"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="833" y="545" width="586" height="502" key="find.popup/0.28.2560.1529@0.28.2560.1529" timestamp="1594972340355" /> | ||
| - <state x="915" y="547" width="415" height="496" key="refactoring.ChangeSignatureDialog" timestamp="1594967617639"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="915" y="547" width="415" height="496" key="refactoring.ChangeSignatureDialog/0.28.2560.1529@0.28.2560.1529" timestamp="1594967617639" /> | ||
| - <state x="589" y="382" width="672" height="677" key="run.anything.popup" timestamp="1594967368555"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="589" y="382" width="672" height="677" key="run.anything.popup/0.28.2560.1529@0.28.2560.1529" timestamp="1594967368555" /> | ||
| - <state x="589" y="477" width="672" height="678" key="search.everywhere.popup" timestamp="1594965859325"> | ||
| - <screen x="0" y="28" width="2560" height="1529" /> | ||
| - </state> | ||
| - <state x="589" y="477" width="672" height="678" key="search.everywhere.popup/0.28.2560.1529@0.28.2560.1529" timestamp="1594965859325" /> | ||
| - </component> | ||
| -</project> | ||
| + |
| dependencies { | ||
| - implementation 'com.kitfox.svg:svg-salamander:1.0' | ||
| + // SVG | ||
| + implementation fileTree(include: ['**/*.jar'], dir: 'libs') | ||
| implementation 'com.1stleg:jnativehook:2.1.0' |
| import com.whitemagicsoftware.kmcaster.listeners.FrameDragListener; | ||
| -import com.whitemagicsoftware.kmcaster.listeners.SwitchName; | ||
| import javax.swing.*; | ||
| import java.awt.*; | ||
| import java.awt.geom.RoundRectangle2D; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| -import static com.whitemagicsoftware.kmcaster.HardwareSwitches.state; | ||
| -import static com.whitemagicsoftware.kmcaster.listeners.SwitchName.*; | ||
| +import static com.whitemagicsoftware.kmcaster.HardwareImages.state; | ||
| +import static com.whitemagicsoftware.kmcaster.HardwareSwitch.*; | ||
| public class EventFrame extends JFrame { | ||
| private static final float ARC = 8; | ||
| private static final Dimension FRAME_DIMENSIONS = new Dimension( 484, 70 ); | ||
| private static final Color TRANSLUCENT = new Color( .2f, .2f, .2f, 0.5f ); | ||
| private static final Color TRANSPARENT = new Color( 0, 0, 0, 0 ); | ||
| - private final HardwareSwitches mSwitches; | ||
| - private final Map<SwitchName, ImageComponent> mSwitchViews = new HashMap<>(); | ||
| + private final HardwareImages mSwitches; | ||
| + private final Map<HardwareSwitch, ImageComponent> mSwitchViews = new HashMap<>(); | ||
| public EventFrame() { | ||
| addMouseMotionListener( frameDragListener ); | ||
| - final var dimensions = new Dimension( getWidth(), getHeight() - 10 ); | ||
| - mSwitches = new HardwareSwitches( dimensions ); | ||
| + final var dimensions = new Dimension( getWidth(), getHeight()- 10 ); | ||
| + mSwitches = new HardwareImages( dimensions ); | ||
| final var mouseImage = mSwitches.get( state( MOUSE_LEFT, false ) ); | ||
| protected void updateSwitchLabel( | ||
| - final SwitchName name, final String label ) { | ||
| + final HardwareSwitch name, final String label ) { | ||
| + System.out.println( "Switch Label: " + label ); | ||
| } | ||
| +/* | ||
| + * Copyright 2020 White Magic Software, Ltd. | ||
| + * | ||
| + * All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions are met: | ||
| + * | ||
| + * o Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * | ||
| + * o Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| +package com.whitemagicsoftware.kmcaster; | ||
| + | ||
| +import java.awt.*; | ||
| +import java.util.HashMap; | ||
| +import java.util.Map; | ||
| + | ||
| +import static com.whitemagicsoftware.kmcaster.HardwareState.ANY_KEY; | ||
| +import static com.whitemagicsoftware.kmcaster.HardwareSwitch.*; | ||
| +import static java.lang.String.format; | ||
| + | ||
| +/** | ||
| + * Responsible for loading vector graphics representations of application | ||
| + * images. The images provide an on-screen interface that indicate to the user | ||
| + * what key or mouse events are being triggered. | ||
| + */ | ||
| +public class HardwareImages { | ||
| + | ||
| + private final static String DIR_IMAGES = "/images"; | ||
| + private final static String DIR_IMAGES_KEYBOARD = DIR_IMAGES + "/key"; | ||
| + private final static String DIR_IMAGES_MOUSE = DIR_IMAGES + "/mouse"; | ||
| + | ||
| + private final static SvgRasterizer sRasterizer = new SvgRasterizer(); | ||
| + | ||
| + private final Map<HardwareState, Image> mImages = new HashMap<>(); | ||
| + private final Dimension mDimension; | ||
| + | ||
| + /** | ||
| + * Constructs an enumerated type that represents the different types of | ||
| + * images shown when keyboard and mouse events are triggered. | ||
| + * | ||
| + * @param dimension The image will be scaled to the given dimensions, aspect | ||
| + * ratio is maintained. | ||
| + */ | ||
| + public HardwareImages( final Dimension dimension ) { | ||
| + assert dimension != null; | ||
| + | ||
| + mDimension = dimension; | ||
| + | ||
| + final var mouseReleased = mouseImage( "0" ); | ||
| + | ||
| + for( int i = 1; i <= 3; i++ ) { | ||
| + final var s = Integer.toString( i ); | ||
| + final var switchName = HardwareSwitch.valueFrom( "button " + s ); | ||
| + mImages.put( state( switchName, true ), mouseImage( s ) ); | ||
| + mImages.put( state( switchName, false ), mouseReleased ); | ||
| + } | ||
| + | ||
| + mImages.put( state( MOUSE_LR, true ), mouseImage( "1-3" ) ); | ||
| + mImages.put( state( MOUSE_LR, false ), mouseReleased ); | ||
| + | ||
| + mImages.put( state( KEY_ALT, true ), keyDnImage( "medium" ) ); | ||
| + mImages.put( state( KEY_ALT, false ), keyUpImage( "medium" ) ); | ||
| + mImages.put( state( KEY_CTRL, true ), keyDnImage( "medium" ) ); | ||
| + mImages.put( state( KEY_CTRL, false ), keyUpImage( "medium" ) ); | ||
| + mImages.put( state( KEY_SHIFT, true ), keyDnImage( "long" ) ); | ||
| + mImages.put( state( KEY_SHIFT, false ), keyUpImage( "long" ) ); | ||
| + mImages.put( state( KEY_REGULAR, ANY_KEY ), keyDnImage( "short" ) ); | ||
| + mImages.put( state( KEY_REGULAR, false ), keyUpImage( "short" ) ); | ||
| + } | ||
| + | ||
| + public Image get( final HardwareState state ) { | ||
| + return mImages.get( state ); | ||
| + } | ||
| + | ||
| + public static HardwareState state( | ||
| + final HardwareSwitch name, final boolean state ) { | ||
| + return state( name, Boolean.toString( state ) ); | ||
| + } | ||
| + | ||
| + public static HardwareState state( | ||
| + final HardwareSwitch name, final String state ) { | ||
| + return new HardwareState( name, state ); | ||
| + } | ||
| + | ||
| + private Image mouseImage( final String prefix ) { | ||
| + return createImage( format( "%s/%s", DIR_IMAGES_MOUSE, prefix ) ); | ||
| + } | ||
| + | ||
| + private Image keyImage( | ||
| + final String state, final String prefix ) { | ||
| + return createImage( | ||
| + format( "%s/%s/%s", DIR_IMAGES_KEYBOARD, state, prefix ) | ||
| + ); | ||
| + } | ||
| + | ||
| + private Image keyUpImage( final String prefix ) { | ||
| + return keyImage( "up", prefix ); | ||
| + } | ||
| + | ||
| + private Image keyDnImage( final String prefix ) { | ||
| + return keyImage( "dn", prefix ); | ||
| + } | ||
| + | ||
| + private Image createImage( final String path ) { | ||
| + assert mDimension != null; | ||
| + | ||
| + final var resource = format( "%s.svg", path ); | ||
| + | ||
| + try { | ||
| + return sRasterizer.rasterize( resource, mDimension ); | ||
| + } catch( final Exception ex ) { | ||
| + rethrow( ex ); | ||
| + } | ||
| + | ||
| + final var msg = format( "Missing resource %s", resource ); | ||
| + throw new RuntimeException( msg ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Cast a checked exception as a {@link RuntimeException}. | ||
| + * | ||
| + * @param <T> What type of {@link Throwable} to throw. | ||
| + * @param t The problem to cast. | ||
| + * @throws T The throwable is casted to this type. | ||
| + */ | ||
| + @SuppressWarnings("unchecked") | ||
| + private static <T extends Throwable> void rethrow( final Throwable t ) | ||
| + throws T { | ||
| + throw (T) t; | ||
| + } | ||
| +} | ||
| package com.whitemagicsoftware.kmcaster; | ||
| -import com.whitemagicsoftware.kmcaster.listeners.SwitchName; | ||
| - | ||
| /** | ||
| * Responsible for defining hardware switch states. | ||
| */ | ||
| public class HardwareState { | ||
| public final static String ANY_KEY = "*"; | ||
| - private final SwitchName mName; | ||
| + private final HardwareSwitch mName; | ||
| private final String mState; | ||
| /** | ||
| * Constructs a new instance that represents whether a key or mouse button | ||
| * was pressed. | ||
| * | ||
| - * @param name The {@link SwitchName} representing the type of switch | ||
| + * @param name The {@link HardwareSwitch} representing the type of switch | ||
| * state to represent. | ||
| * @param state A value of "*' means a regular key was pressed; otherwise, | ||
| * "true" or "false" indicate pressed or released, respectively. | ||
| */ | ||
| - public HardwareState( final SwitchName name, final String state ) { | ||
| + public HardwareState( final HardwareSwitch name, final String state ) { | ||
| assert name != null; | ||
| assert valid( state ); | ||
| mName = name; | ||
| mState = state; | ||
| } | ||
| - public SwitchName getKey() { | ||
| + public HardwareSwitch getKey() { | ||
| return mName; | ||
| } |
| +/* | ||
| + * Copyright 2020 White Magic Software, Ltd. | ||
| + * | ||
| + * All rights reserved. | ||
| + * | ||
| + * Redistribution and use in source and binary forms, with or without | ||
| + * modification, are permitted provided that the following conditions are met: | ||
| + * | ||
| + * o Redistributions of source code must retain the above copyright | ||
| + * notice, this list of conditions and the following disclaimer. | ||
| + * | ||
| + * o Redistributions in binary form must reproduce the above copyright | ||
| + * notice, this list of conditions and the following disclaimer in the | ||
| + * documentation and/or other materials provided with the distribution. | ||
| + * | ||
| + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| + * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| + */ | ||
| +package com.whitemagicsoftware.kmcaster; | ||
| + | ||
| +import static org.jnativehook.NativeInputEvent.*; | ||
| + | ||
| +/** | ||
| + * Used for compile-time binding between change listeners input events. | ||
| + */ | ||
| +public enum HardwareSwitch { | ||
| + KEY_ALT( "alt", ALT_MASK ), | ||
| + KEY_CTRL( "ctrl", CTRL_MASK ), | ||
| + KEY_SHIFT( "shift", SHIFT_MASK ), | ||
| + KEY_REGULAR( "regular" ), | ||
| + MOUSE_LEFT( "button 1" ), | ||
| + MOUSE_WHEEL( "button 2" ), | ||
| + MOUSE_RIGHT( "button 3" ), | ||
| + MOUSE_LR( "button 1-3" ); | ||
| + | ||
| + /** | ||
| + * Indicates the switch is not a modifier. | ||
| + */ | ||
| + private final static int NO_MASK = -1; | ||
| + | ||
| + private final String mName; | ||
| + private final int mMask; | ||
| + | ||
| + /** | ||
| + * Constructs a new switch with no mask value. | ||
| + * | ||
| + * @param name The switch name. | ||
| + */ | ||
| + HardwareSwitch( final String name ) { | ||
| + this( name, NO_MASK ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Constructs a new switch associated with a mask value that can be used | ||
| + * to determine whether a modifier key is pressed. | ||
| + * | ||
| + * @param name The switch name. | ||
| + * @param mask Modifier key bitmask. | ||
| + */ | ||
| + HardwareSwitch( final String name, final int mask ) { | ||
| + mName = name; | ||
| + mMask = mask; | ||
| + } | ||
| + | ||
| + /** | ||
| + * Answers whether this enumerated item represents a keyboard modifier. | ||
| + * | ||
| + * @return {@code true} when the switch is a modifier key. | ||
| + */ | ||
| + public boolean isModifier() { | ||
| + return this == KEY_ALT || this == KEY_CTRL || this == KEY_SHIFT; | ||
| + } | ||
| + | ||
| + /** | ||
| + * Answers whether the given name and the switch's name are the same, | ||
| + * ignoring case. | ||
| + * | ||
| + * @param name The switch name to compare against this name. | ||
| + * @return {@code true} when the names match, regardless of case. | ||
| + */ | ||
| + public boolean isName( final String name ) { | ||
| + return mName.equalsIgnoreCase( name ); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Answers whether this hardware switch is pressed, but only applies to | ||
| + * keyboard modifier keys, not mouse buttons. | ||
| + * | ||
| + * @param modifiers A set of bits that indicate what modifier keys are | ||
| + * pressed. | ||
| + * @return {@code true} if this switch's modifier bit is set in the | ||
| + * given {@code modifiers} value. | ||
| + */ | ||
| + public boolean isPressed( final int modifiers ) { | ||
| + assert isModifier(); | ||
| + | ||
| + return (modifiers & mMask) != 0; | ||
| + } | ||
| + | ||
| + /** | ||
| + * Looks up the key that matches the given name, case-insensitively. | ||
| + * | ||
| + * @param name The name of the key to find in this enum. | ||
| + * @return The {@link HardwareSwitch} object that matches the name. | ||
| + */ | ||
| + public static HardwareSwitch valueFrom( final String name ) { | ||
| + for( final var b : HardwareSwitch.values() ) { | ||
| + if( b.isName( name ) ) { | ||
| + return b; | ||
| + } | ||
| + } | ||
| + | ||
| + return KEY_REGULAR; | ||
| + } | ||
| + | ||
| + /** | ||
| + * Returns the switch name. | ||
| + * | ||
| + * @return The switch name, nothing more. | ||
| + */ | ||
| + @Override | ||
| + public String toString() { | ||
| + return mName; | ||
| + } | ||
| + | ||
| +} | ||
| -/* | ||
| - * Copyright 2020 White Magic Software, Ltd. | ||
| - * | ||
| - * All rights reserved. | ||
| - * | ||
| - * Redistribution and use in source and binary forms, with or without | ||
| - * modification, are permitted provided that the following conditions are met: | ||
| - * | ||
| - * o Redistributions of source code must retain the above copyright | ||
| - * notice, this list of conditions and the following disclaimer. | ||
| - * | ||
| - * o Redistributions in binary form must reproduce the above copyright | ||
| - * notice, this list of conditions and the following disclaimer in the | ||
| - * documentation and/or other materials provided with the distribution. | ||
| - * | ||
| - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| - */ | ||
| -package com.whitemagicsoftware.kmcaster; | ||
| - | ||
| -import com.whitemagicsoftware.kmcaster.listeners.SwitchName; | ||
| - | ||
| -import java.awt.*; | ||
| -import java.util.HashMap; | ||
| -import java.util.Map; | ||
| - | ||
| -import static com.whitemagicsoftware.kmcaster.HardwareState.ANY_KEY; | ||
| -import static com.whitemagicsoftware.kmcaster.listeners.SwitchName.*; | ||
| -import static java.lang.String.format; | ||
| - | ||
| -/** | ||
| - * Responsible for loading vector graphics representations of application | ||
| - * images. The images provide an on-screen interface that indicate to the user | ||
| - * what key or mouse events are being triggered. | ||
| - */ | ||
| -public class HardwareSwitches { | ||
| - | ||
| - private final static String DIR_IMAGES = "/images"; | ||
| - private final static String DIR_IMAGES_KEYBOARD = DIR_IMAGES + "/key"; | ||
| - private final static String DIR_IMAGES_MOUSE = DIR_IMAGES + "/mouse"; | ||
| - | ||
| - private final static SvgRasterizer sRasterizer = new SvgRasterizer(); | ||
| - | ||
| - private final Map<HardwareState, Image> mImages = new HashMap<>(); | ||
| - private final Dimension mDimension; | ||
| - | ||
| - /** | ||
| - * Constructs an enumerated type that represents the different types of | ||
| - * images shown when keyboard and mouse events are triggered. | ||
| - * | ||
| - * @param dimension The image will be scaled to the given height, aspect | ||
| - * ratio is maintained. | ||
| - */ | ||
| - public HardwareSwitches( final Dimension dimension ) { | ||
| - assert dimension != null; | ||
| - | ||
| - mDimension = dimension; | ||
| - | ||
| - final var mouseReleased = mouseImage( "0" ); | ||
| - | ||
| - for( int i = 1; i <= 3; i++ ) { | ||
| - final var s = Integer.toString( i ); | ||
| - final var switchName = SwitchName.valueFrom( "button " + s ); | ||
| - mImages.put( state( switchName, true ), mouseImage( s ) ); | ||
| - mImages.put( state( switchName, false ), mouseReleased ); | ||
| - } | ||
| - | ||
| - mImages.put( state( MOUSE_LR, true ), mouseImage( "1-3" ) ); | ||
| - mImages.put( state( MOUSE_LR, false ), mouseReleased ); | ||
| - | ||
| - mImages.put( state( KEY_ALT, true ), keyDnImage( "medium" ) ); | ||
| - mImages.put( state( KEY_ALT, false ), keyUpImage( "medium" ) ); | ||
| - mImages.put( state( KEY_CTRL, true ), keyDnImage( "medium" ) ); | ||
| - mImages.put( state( KEY_CTRL, false ), keyUpImage( "medium" ) ); | ||
| - mImages.put( state( KEY_SHIFT, true ), keyDnImage( "long" ) ); | ||
| - mImages.put( state( KEY_SHIFT, false ), keyUpImage( "long" ) ); | ||
| - mImages.put( state( KEY_REGULAR, ANY_KEY ), keyDnImage( "short" ) ); | ||
| - mImages.put( state( KEY_REGULAR, false ), keyUpImage( "short" ) ); | ||
| - } | ||
| - | ||
| - public Image get( final HardwareState state ) { | ||
| - return mImages.get( state ); | ||
| - } | ||
| - | ||
| - public static HardwareState state( | ||
| - final SwitchName name, final boolean state ) { | ||
| - return state( name, Boolean.toString( state ) ); | ||
| - } | ||
| - | ||
| - public static HardwareState state( | ||
| - final SwitchName name, final String state ) { | ||
| - return new HardwareState( name, state ); | ||
| - } | ||
| - | ||
| - private Image mouseImage( final String prefix ) { | ||
| - return createImage( format( "%s/%s", DIR_IMAGES_MOUSE, prefix ) ); | ||
| - } | ||
| - | ||
| - private Image keyImage( | ||
| - final String state, final String prefix ) { | ||
| - return createImage( | ||
| - format( "%s/%s/%s", DIR_IMAGES_KEYBOARD, state, prefix ) | ||
| - ); | ||
| - } | ||
| - | ||
| - private Image keyUpImage( final String prefix ) { | ||
| - return keyImage( "up", prefix ); | ||
| - } | ||
| - | ||
| - private Image keyDnImage( final String prefix ) { | ||
| - return keyImage( "dn", prefix ); | ||
| - } | ||
| - | ||
| - private Image createImage( final String path ) { | ||
| - assert mDimension != null; | ||
| - | ||
| - return sRasterizer.rasterize( format( "%s.svg", path ), mDimension ); | ||
| - } | ||
| -} | ||
| import com.whitemagicsoftware.kmcaster.listeners.KeyboardListener; | ||
| import com.whitemagicsoftware.kmcaster.listeners.MouseListener; | ||
| -import com.whitemagicsoftware.kmcaster.listeners.SwitchName; | ||
| import org.jnativehook.GlobalScreen; | ||
| +import org.jnativehook.NativeHookException; | ||
| import java.beans.PropertyChangeEvent; | ||
| } | ||
| + /** | ||
| + * Called when a hardware switch has changed state. | ||
| + * | ||
| + * @param e Contains the identifier for the switch, its previous value, | ||
| + * and its new value. | ||
| + */ | ||
| @Override | ||
| public void propertyChange( final PropertyChangeEvent e ) { | ||
| - var switchValue = e.getNewValue().toString(); | ||
| + final var switchName = e.getPropertyName(); | ||
| + final var switchValue = e.getNewValue().toString(); | ||
| // True or false indicates a non-regular key was pressed. | ||
| - if( !"false".equals( switchValue ) && !"true".equals( switchValue ) ) { | ||
| - switchValue = ANY_KEY; | ||
| - } | ||
| + final var context = | ||
| + (!"false".equals( switchValue ) && !"true".equals( switchValue )) | ||
| + ? ANY_KEY | ||
| + : switchValue; | ||
| - final var switchState = createState( e.getPropertyName(), switchValue ); | ||
| + final var switchState = createState( e.getPropertyName(), context ); | ||
| updateSwitchState( switchState ); | ||
| updateSwitchLabel( switchState.getKey(), switchValue ); | ||
| } | ||
| private HardwareState createState( | ||
| final String name, final String state ) { | ||
| assert name != null; | ||
| assert state != null; | ||
| - final var key = SwitchName.valueFrom( name ); | ||
| + final var key = HardwareSwitch.valueFrom( name ); | ||
| return new HardwareState( key, state ); | ||
| } | ||
| /** | ||
| * Initialize the key and mouse event listener native interface. | ||
| */ | ||
| - private static void initNativeHook() { | ||
| - try { | ||
| - registerNativeHook(); | ||
| + private static void initNativeHook() throws NativeHookException { | ||
| + registerNativeHook(); | ||
| - final var logger = getLogger( GlobalScreen.class.getPackage().getName() ); | ||
| - logger.setLevel( Level.OFF ); | ||
| - logger.setUseParentHandlers( false ); | ||
| - } catch( final Exception ex ) { | ||
| - rethrow( ex ); | ||
| - } | ||
| + final var logger = getLogger( GlobalScreen.class.getPackage().getName() ); | ||
| + logger.setLevel( Level.OFF ); | ||
| + logger.setUseParentHandlers( false ); | ||
| } | ||
| /** | ||
| * Main entry point. | ||
| * | ||
| * @param args Unused. | ||
| */ | ||
| - public static void main( final String[] args ) { | ||
| + public static void main( final String[] args ) throws NativeHookException { | ||
| initNativeHook(); | ||
| invokeLater( () -> { | ||
| final var kc = new KmCaster(); | ||
| kc.setVisible( true ); | ||
| } ); | ||
| - } | ||
| - | ||
| - /** | ||
| - * Cast a checked exception as a {@link RuntimeException}. | ||
| - * | ||
| - * @param <T> What type of {@link Throwable} to throw. | ||
| - * @param throwable The problem to cast. | ||
| - * @throws T The throwable is casted to this type. | ||
| - */ | ||
| - @SuppressWarnings("unchecked") | ||
| - public static <T extends Throwable> void rethrow( final Throwable throwable ) | ||
| - throws T { | ||
| - throw (T) throwable; | ||
| } | ||
| } | ||
| import java.awt.*; | ||
| -import java.awt.geom.AffineTransform; | ||
| import java.awt.image.BufferedImage; | ||
| import java.net.URL; | ||
| import java.util.Map; | ||
| -import static com.whitemagicsoftware.kmcaster.KmCaster.rethrow; | ||
| import static java.awt.RenderingHints.*; | ||
| -import static java.awt.RenderingHints.VALUE_TEXT_ANTIALIAS_ON; | ||
| import static java.awt.image.BufferedImage.TYPE_INT_ARGB; | ||
| * @param dstDim The output image dimensions. | ||
| * @return The rasterized {@link Image}. | ||
| + * @throws SVGException Could not open, read, parse, or render SVG data. | ||
| */ | ||
| - public Image rasterize( final String path, final Dimension dstDim ) { | ||
| + public Image rasterize( final String path, final Dimension dstDim ) | ||
| + throws SVGException { | ||
| final var diagram = loadDiagram( path ); | ||
| - final var diaWidth = diagram.getWidth(); | ||
| - final var diaHeight = diagram.getHeight(); | ||
| - final var srcDim = new Dimension( (int) diaWidth, (int) diaHeight ); | ||
| + final var wDiagram = diagram.getWidth(); | ||
| + final var hDiagram = diagram.getHeight(); | ||
| + final var srcDim = new Dimension( (int) wDiagram, (int) hDiagram ); | ||
| - final var scaledDim = scale( srcDim, dstDim ); | ||
| - final var w = (int) scaledDim.getWidth(); | ||
| - final var h = (int) scaledDim.getHeight(); | ||
| + final var scaled = fit( srcDim, dstDim ); | ||
| + final var wScaled = (int) scaled.getWidth(); | ||
| + final var hScaled = (int) scaled.getHeight(); | ||
| - final var image = new BufferedImage( w, h, TYPE_INT_ARGB ); | ||
| + final var image = new BufferedImage( wScaled, hScaled, TYPE_INT_ARGB ); | ||
| - try { | ||
| - final Graphics2D g = image.createGraphics(); | ||
| - g.setRenderingHints( RENDERING_HINTS ); | ||
| + final var g = image.createGraphics(); | ||
| + g.setRenderingHints( RENDERING_HINTS ); | ||
| - final AffineTransform transform = g.getTransform(); | ||
| - transform.setToScale( w / diaWidth, h / diaHeight ); | ||
| + final var transform = g.getTransform(); | ||
| + transform.setToScale( wScaled / wDiagram, hScaled / hDiagram ); | ||
| - g.setTransform( transform ); | ||
| - diagram.render( g ); | ||
| - g.dispose(); | ||
| - } catch( final SVGException e ) { | ||
| - rethrow( e ); | ||
| - } | ||
| + g.setTransform( transform ); | ||
| + diagram.render( g ); | ||
| + g.dispose(); | ||
| return image; | ||
| } | ||
| + /** | ||
| + * Gets an instance of {@link URL} that references a file in the | ||
| + * application's resources. | ||
| + * | ||
| + * @param path The full path (starting at the root), relative to the | ||
| + * application or JAR file's resources directory. | ||
| + * @return A {@link URL} to the file or {@code null} if the path does not | ||
| + * point to a resource. | ||
| + */ | ||
| private URL getResourceUrl( final String path ) { | ||
| - return HardwareSwitches.class.getResource( path ); | ||
| + return SvgRasterizer.class.getResource( path ); | ||
| } | ||
| + /** | ||
| + * Loads the resource specified by the given path into an instance of | ||
| + * {@link SVGDiagram} that can be rasterized into a bitmap format. The | ||
| + * {@link SVGUniverse} class will | ||
| + * | ||
| + * @param path The full path (starting at the root), relative to the | ||
| + * application or JAR file's resources directory. | ||
| + * @return An {@link SVGDiagram} that can be rasterized onto a | ||
| + * {@link BufferedImage}. | ||
| + */ | ||
| private SVGDiagram loadDiagram( final String path ) { | ||
| final var url = getResourceUrl( path ); | ||
| - return applySettings( sRenderer.getDiagram( sRenderer.loadSVG( url ) ) ); | ||
| + final var uri = sRenderer.loadSVG( url ); | ||
| + final var diagram = sRenderer.getDiagram( uri ); | ||
| + return applySettings( diagram ); | ||
| } | ||
| + /** | ||
| + * Instructs the SVG renderer to rasterize the image even if it would be | ||
| + * clipped. | ||
| + * | ||
| + * @param diagram The {@link SVGDiagram} to render. | ||
| + * @return The same instance with ignore clip heuristics set to {@code true}. | ||
| + */ | ||
| private SVGDiagram applySettings( final SVGDiagram diagram ) { | ||
| diagram.setIgnoringClipHeuristic( true ); | ||
| return diagram; | ||
| } | ||
| - private Dimension scale( final Dimension src, final Dimension dst ) { | ||
| + /** | ||
| + * Scales the given source {@link Dimension} to the destination | ||
| + * {@link Dimension}, maintaining the aspect ratio with respect to | ||
| + * the best fit. | ||
| + * | ||
| + * @param src The original vector graphic dimensions to change. | ||
| + * @param dst The desired image dimensions to scale. | ||
| + * @return The given source dimensions scaled to the destination dimensions, | ||
| + * maintaining the aspect ratio. | ||
| + */ | ||
| + private Dimension fit( final Dimension src, final Dimension dst ) { | ||
| final var srcWidth = src.getWidth(); | ||
| final var srcHeight = src.getHeight(); | ||
| - final var dstHeight = dst.getHeight(); | ||
| - | ||
| - var newWidth = srcWidth; | ||
| - var newHeight = srcHeight; | ||
| - if( newHeight < dstHeight ) { | ||
| - newHeight = dstHeight; | ||
| - newWidth = (newHeight * srcWidth) / srcHeight; | ||
| - } | ||
| + // Determine the ratio that will have the best fit. | ||
| + final var ratio = Math.min( | ||
| + dst.getWidth() / srcWidth, dst.getHeight() / srcHeight | ||
| + ); | ||
| - return new Dimension( (int) newWidth, (int) newHeight ); | ||
| + // Scale both dimensions with respect to the best fit ratio. | ||
| + return new Dimension( (int) (srcWidth * ratio), (int) (srcHeight * ratio) ); | ||
| } | ||
| } | ||
| import java.awt.event.MouseEvent; | ||
| +/** | ||
| + * Responsible for moving the window when the user drags it around the screen. | ||
| + */ | ||
| public class FrameDragListener extends MouseAdapter { | ||
| private final JFrame mFrame; |
| package com.whitemagicsoftware.kmcaster.listeners; | ||
| +import com.whitemagicsoftware.kmcaster.HardwareSwitch; | ||
| import org.jnativehook.keyboard.NativeKeyEvent; | ||
| import org.jnativehook.keyboard.NativeKeyListener; | ||
| import java.beans.PropertyChangeListener; | ||
| import java.beans.PropertyChangeSupport; | ||
| -import java.util.List; | ||
| +import java.util.HashMap; | ||
| import java.util.Map; | ||
| -import static com.whitemagicsoftware.kmcaster.listeners.SwitchName.*; | ||
| +import static com.whitemagicsoftware.kmcaster.HardwareSwitch.*; | ||
| import static java.util.Map.entry; | ||
| -import static org.jnativehook.NativeInputEvent.*; | ||
| import static org.jnativehook.keyboard.NativeKeyEvent.getKeyText; | ||
| +/** | ||
| + * Responsible for sending property change events when the keyboard state | ||
| + * changes. | ||
| + */ | ||
| public class KeyboardListener implements NativeKeyListener { | ||
| - private final List<KeyboardModifier> mModifiers = List.of( | ||
| - new KeyboardModifier( KEY_ALT, ALT_MASK ), | ||
| - new KeyboardModifier( KEY_CTRL, CTRL_MASK ), | ||
| - new KeyboardModifier( KEY_SHIFT, SHIFT_MASK ) | ||
| - ); | ||
| - | ||
| @SuppressWarnings("RedundantTypeArguments") | ||
| private final static Map<Integer, String> KEY_CODES = | ||
| ); | ||
| - private String mRegularHeld = ""; | ||
| + private final Map<HardwareSwitch, Boolean> mModifiers = new HashMap<>(); | ||
| private final PropertyChangeSupport mDispatcher = | ||
| new PropertyChangeSupport( this ); | ||
| + | ||
| + private String mRegularHeld = ""; | ||
| public KeyboardListener() { | ||
| + mModifiers.put( KEY_ALT, false ); | ||
| + mModifiers.put( KEY_CTRL, false ); | ||
| + mModifiers.put( KEY_SHIFT, false ); | ||
| } | ||
| // The key is regular iff its name does not match any modifier name. | ||
| - for( final var modifier : mModifiers ) { | ||
| - isModifier |= (modifier.isKeyName( n ) || modifier.isKeyName( o )); | ||
| + for( final var key : mModifiers.keySet() ) { | ||
| + isModifier |= (key.isName( n ) || key.isName( o )); | ||
| } | ||
| */ | ||
| private void updateModifiers( final NativeKeyEvent e ) { | ||
| - for( final var modifier : mModifiers ) { | ||
| - final boolean down = modifier.matches( e ); | ||
| - tryFire( modifier.getKey(), modifier.isHeld(), down ); | ||
| - modifier.setHeld( down ); | ||
| + for( final var key : mModifiers.keySet() ) { | ||
| + final boolean down = key.isPressed( e.getModifiers() ); | ||
| + tryFire( key, mModifiers.get( key ), down ); | ||
| + mModifiers.put( key, down ); | ||
| } | ||
| } | ||
| * @param n New property value. | ||
| */ | ||
| - private void tryFire( final SwitchName key, final String o, final String n ) { | ||
| + private void tryFire( final HardwareSwitch key, final String o, | ||
| + final String n ) { | ||
| if( !o.equals( n ) ) { | ||
| mDispatcher.firePropertyChange( key.toString(), o, n ); | ||
| } | ||
| } | ||
| /** | ||
| - * Delegates to {@link #tryFire(SwitchName, String, String)} with {@link Boolean} | ||
| + * Delegates to {@link #tryFire(HardwareSwitch, String, String)} with | ||
| + * {@link Boolean} | ||
| * values as strings. | ||
| * | ||
| * @param key The name of the property that has changed. | ||
| * @param o Old property value. | ||
| * @param n New property value. | ||
| */ | ||
| - private void tryFire( final SwitchName key, final boolean o, final boolean n ) { | ||
| + private void tryFire( final HardwareSwitch key, final boolean o, | ||
| + final boolean n ) { | ||
| tryFire( key, Boolean.toString( o ), Boolean.toString( n ) ); | ||
| } | ||
| -/* | ||
| - * Copyright 2020 White Magic Software, Ltd. | ||
| - * | ||
| - * All rights reserved. | ||
| - * | ||
| - * Redistribution and use in source and binary forms, with or without | ||
| - * modification, are permitted provided that the following conditions are met: | ||
| - * | ||
| - * o Redistributions of source code must retain the above copyright | ||
| - * notice, this list of conditions and the following disclaimer. | ||
| - * | ||
| - * o Redistributions in binary form must reproduce the above copyright | ||
| - * notice, this list of conditions and the following disclaimer in the | ||
| - * documentation and/or other materials provided with the distribution. | ||
| - * | ||
| - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| - */ | ||
| -package com.whitemagicsoftware.kmcaster.listeners; | ||
| - | ||
| -import org.jnativehook.keyboard.NativeKeyEvent; | ||
| - | ||
| -/** | ||
| - * Responsible for tracking the state of modifier keys. | ||
| - */ | ||
| -public class KeyboardModifier { | ||
| - private boolean mHeld; | ||
| - private final int mMask; | ||
| - private final SwitchName mKey; | ||
| - | ||
| - KeyboardModifier( final SwitchName key, final int mask ) { | ||
| - assert key != null; | ||
| - | ||
| - mKey = key; | ||
| - mMask = mask; | ||
| - } | ||
| - | ||
| - public boolean isKeyName( final String key ) { | ||
| - return getKey().isName( key ); | ||
| - } | ||
| - | ||
| - public boolean isHeld() { | ||
| - return mHeld; | ||
| - } | ||
| - | ||
| - public void setHeld( final boolean held ) { | ||
| - mHeld = held; | ||
| - } | ||
| - | ||
| - public SwitchName getKey() { | ||
| - return mKey; | ||
| - } | ||
| - | ||
| - /** | ||
| - * Answers whether the given {@link NativeKeyEvent} modifiers are set to | ||
| - * include the modifiers defined by this instance. | ||
| - * | ||
| - * @param e The event that has modifiers to check. | ||
| - * @return {@code true} This modifier matches the given event's modifiers. | ||
| - */ | ||
| - public boolean matches( final NativeKeyEvent e ) { | ||
| - return (e.getModifiers() & mMask) != 0; | ||
| - } | ||
| - | ||
| - public String toString() { | ||
| - return getKey().toString(); | ||
| - } | ||
| -} | ||
| -/* | ||
| - * Copyright 2020 White Magic Software, Ltd. | ||
| - * | ||
| - * All rights reserved. | ||
| - * | ||
| - * Redistribution and use in source and binary forms, with or without | ||
| - * modification, are permitted provided that the following conditions are met: | ||
| - * | ||
| - * o Redistributions of source code must retain the above copyright | ||
| - * notice, this list of conditions and the following disclaimer. | ||
| - * | ||
| - * o Redistributions in binary form must reproduce the above copyright | ||
| - * notice, this list of conditions and the following disclaimer in the | ||
| - * documentation and/or other materials provided with the distribution. | ||
| - * | ||
| - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
| - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| - * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
| - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
| - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
| - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
| - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
| - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
| - */ | ||
| -package com.whitemagicsoftware.kmcaster.listeners; | ||
| - | ||
| -/** | ||
| - * Used for compile-time binding between change listeners input events. | ||
| - */ | ||
| -public enum SwitchName { | ||
| - KEY_ALT( "alt" ), | ||
| - KEY_CTRL( "ctrl" ), | ||
| - KEY_SHIFT( "shift" ), | ||
| - KEY_REGULAR( "regular" ), | ||
| - MOUSE_LEFT( "button 1" ), | ||
| - MOUSE_WHEEL( "button 2" ), | ||
| - MOUSE_RIGHT( "button 3" ), | ||
| - MOUSE_LR( "button 1-3" ); | ||
| - | ||
| - private final String mName; | ||
| - | ||
| - SwitchName( final String name ) { | ||
| - mName = name; | ||
| - } | ||
| - | ||
| - public boolean isName( final String name ) { | ||
| - return mName.equalsIgnoreCase( name ); | ||
| - } | ||
| - | ||
| - /** | ||
| - * Looks up the key that matches the given name, case-insensitively. | ||
| - * | ||
| - * @param name The name of the key to find in this enum. | ||
| - * @return The {@link SwitchName} object that matches the name. | ||
| - */ | ||
| - public static SwitchName valueFrom( final String name ) { | ||
| - for( final var b : SwitchName.values() ) { | ||
| - if( b.isName( name ) ) { | ||
| - return b; | ||
| - } | ||
| - } | ||
| - | ||
| - return KEY_REGULAR; | ||
| - } | ||
| - | ||
| - @Override | ||
| - public String toString() { | ||
| - return mName; | ||
| - } | ||
| -} | ||
| Author | DaveJarvis <email> |
|---|---|
| Date | 2020-07-17 15:42:09 GMT-0700 |
| Commit | cc2f14b8dc109344f53960e6302f96160624e07b |
| Parent | 27637a3 |
| Delta | 412 lines added, 596 lines removed, 184-line decrease |