001/* 002 * Forge Mod Loader 003 * Copyright (c) 2012-2013 cpw. 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser Public License v2.1 006 * which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html 008 * 009 * Contributors: 010 * cpw - implementation 011 */ 012 013package cpw.mods.fml.client.registry; 014 015import java.util.ArrayList; 016import java.util.EnumSet; 017import java.util.Set; 018 019import net.minecraft.client.settings.GameSettings; 020import net.minecraft.client.settings.KeyBinding; 021 022import org.lwjgl.input.Keyboard; 023import org.lwjgl.input.Mouse; 024 025import com.google.common.collect.Lists; 026import com.google.common.collect.Sets; 027 028import cpw.mods.fml.common.ITickHandler; 029import cpw.mods.fml.common.TickType; 030import cpw.mods.fml.common.registry.TickRegistry; 031import cpw.mods.fml.relauncher.Side; 032 033public class KeyBindingRegistry 034{ 035 /** 036 * Register a KeyHandler to the game. This handler will be called on certain tick events 037 * if any of its key is inactive or has recently changed state 038 * 039 * @param handler 040 */ 041 public static void registerKeyBinding(KeyHandler handler) { 042 instance().keyHandlers.add(handler); 043 if (!handler.isDummy) 044 { 045 TickRegistry.registerTickHandler(handler, Side.CLIENT); 046 } 047 } 048 049 050 /** 051 * Extend this class to register a KeyBinding and recieve callback 052 * when the key binding is triggered 053 * 054 * @author cpw 055 * 056 */ 057 public static abstract class KeyHandler implements ITickHandler 058 { 059 protected KeyBinding[] keyBindings; 060 protected boolean[] keyDown; 061 protected boolean[] repeatings; 062 private boolean isDummy; 063 064 /** 065 * Pass an array of keybindings and a repeat flag for each one 066 * 067 * @param keyBindings 068 * @param repeatings 069 */ 070 public KeyHandler(KeyBinding[] keyBindings, boolean[] repeatings) 071 { 072 assert keyBindings.length == repeatings.length : "You need to pass two arrays of identical length"; 073 this.keyBindings = keyBindings; 074 this.repeatings = repeatings; 075 this.keyDown = new boolean[keyBindings.length]; 076 } 077 078 079 /** 080 * Register the keys into the system. You will do your own keyboard management elsewhere. No events will fire 081 * if you use this method 082 * 083 * @param keyBindings 084 */ 085 public KeyHandler(KeyBinding[] keyBindings) 086 { 087 this.keyBindings = keyBindings; 088 this.isDummy = true; 089 } 090 091 public KeyBinding[] getKeyBindings() 092 { 093 return this.keyBindings; 094 } 095 096 /** 097 * Not to be overridden - KeyBindings are tickhandlers under the covers 098 */ 099 @Override 100 public final void tickStart(EnumSet<TickType> type, Object... tickData) 101 { 102 keyTick(type, false); 103 } 104 105 /** 106 * Not to be overridden - KeyBindings are tickhandlers under the covers 107 */ 108 @Override 109 public final void tickEnd(EnumSet<TickType> type, Object... tickData) 110 { 111 keyTick(type, true); 112 } 113 114 private void keyTick(EnumSet<TickType> type, boolean tickEnd) 115 { 116 for (int i = 0; i < keyBindings.length; i++) 117 { 118 KeyBinding keyBinding = keyBindings[i]; 119 int keyCode = keyBinding.keyCode; 120 boolean state = (keyCode < 0 ? Mouse.isButtonDown(keyCode + 100) : Keyboard.isKeyDown(keyCode)); 121 if (state != keyDown[i] || (state && repeatings[i])) 122 { 123 if (state) 124 { 125 keyDown(type, keyBinding, tickEnd, state!=keyDown[i]); 126 } 127 else 128 { 129 keyUp(type, keyBinding, tickEnd); 130 } 131 if (tickEnd) 132 { 133 keyDown[i] = state; 134 } 135 } 136 137 } 138 } 139 140 /** 141 * Called when the key is first in the down position on any tick from the {@link #ticks()} 142 * set. Will be called subsequently with isRepeat set to true 143 * 144 * @see #keyUp(EnumSet, KeyBinding, boolean) 145 * 146 * @param types the type(s) of tick that fired when this key was first down 147 * @param tickEnd was it an end or start tick which fired the key 148 * @param isRepeat is it a repeat key event 149 */ 150 public abstract void keyDown(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd, boolean isRepeat); 151 /** 152 * Fired once when the key changes state from down to up 153 * 154 * @see #keyDown(EnumSet, KeyBinding, boolean, boolean) 155 * 156 * @param types the type(s) of tick that fired when this key was first down 157 * @param tickEnd was it an end or start tick which fired the key 158 */ 159 public abstract void keyUp(EnumSet<TickType> types, KeyBinding kb, boolean tickEnd); 160 161 162 /** 163 * This is the list of ticks for which the key binding should trigger. The only 164 * valid ticks are client side ticks, obviously. 165 * 166 * @see cpw.mods.fml.common.ITickHandler#ticks() 167 */ 168 public abstract EnumSet<TickType> ticks(); 169 } 170 171 private static final KeyBindingRegistry INSTANCE = new KeyBindingRegistry(); 172 173 private Set<KeyHandler> keyHandlers = Sets.newLinkedHashSet(); 174 175 @Deprecated 176 public static KeyBindingRegistry instance() 177 { 178 return INSTANCE; 179 } 180 181 182 public void uploadKeyBindingsToGame(GameSettings settings) 183 { 184 ArrayList<KeyBinding> harvestedBindings = Lists.newArrayList(); 185 for (KeyHandler key : keyHandlers) 186 { 187 for (KeyBinding kb : key.keyBindings) 188 { 189 harvestedBindings.add(kb); 190 } 191 } 192 KeyBinding[] modKeyBindings = harvestedBindings.toArray(new KeyBinding[harvestedBindings.size()]); 193 KeyBinding[] allKeys = new KeyBinding[settings.keyBindings.length + modKeyBindings.length]; 194 System.arraycopy(settings.keyBindings, 0, allKeys, 0, settings.keyBindings.length); 195 System.arraycopy(modKeyBindings, 0, allKeys, settings.keyBindings.length, modKeyBindings.length); 196 settings.keyBindings = allKeys; 197 settings.loadOptions(); 198 } 199}