Skip to content

Commit 5c37a76

Browse files
initial commit
1 parent 15d1963 commit 5c37a76

File tree

11 files changed

+838
-3
lines changed

11 files changed

+838
-3
lines changed

CONTRIBUTING.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# Contributing to Input Language Screamer
2+
3+
Thank you for your interest in contributing to Input Language Screamer! We welcome contributions from the community.
4+
5+
## 🚀 Getting Started
6+
7+
### Prerequisites
8+
- Visual Studio 2022 or VS Code
9+
- .NET 9.0 SDK
10+
- Windows 10/11 for testing
11+
- Git
12+
13+
### Setting Up Development Environment
14+
15+
1. **Fork the repository**
16+
```bash
17+
# Fork on GitHub, then clone your fork
18+
git clone https://github.com/YOUR_USERNAME/OneBitSoftware.InputLanguageScreamer.git
19+
cd OneBitSoftware.InputLanguageScreamer
20+
```
21+
22+
2. **Build the project**
23+
```bash
24+
dotnet build
25+
```
26+
27+
3. **Run the application**
28+
```bash
29+
dotnet run --project Desktop/Desktop.csproj
30+
```
31+
32+
## 🛠️ Development Guidelines
33+
34+
### Code Style
35+
- Follow C# coding conventions
36+
- Use meaningful variable and method names
37+
- Add XML documentation comments for public APIs
38+
- Keep methods focused and single-purpose
39+
40+
### Architecture
41+
- **`LanguageMonitorApp`**: System tray and application lifecycle
42+
- **`GlobalKeyboardHook`**: Windows API keyboard monitoring
43+
- **`LanguageAudioPlayer`**: Audio file management and playback
44+
- **`Program`**: Application entry point
45+
46+
### Adding New Features
47+
48+
1. **Create a feature branch**
49+
```bash
50+
git checkout -b feature/your-feature-name
51+
```
52+
53+
2. **Make your changes**
54+
- Follow existing code patterns
55+
- Add appropriate error handling
56+
- Include XML documentation
57+
58+
3. **Test your changes**
59+
- Build the project: `dotnet build`
60+
- Test manually with different languages
61+
- Ensure no regressions
62+
63+
4. **Commit and push**
64+
```bash
65+
git add .
66+
git commit -m "Add: Your feature description"
67+
git push origin feature/your-feature-name
68+
```
69+
70+
5. **Create a Pull Request**
71+
72+
## 🎵 Adding Language Support
73+
74+
To add support for a new language:
75+
76+
1. **Identify the language name**
77+
- Check how Windows reports the language in `InputLanguage.Culture.EnglishName`
78+
- Example: "Spanish (Spain)" → "Spanish"
79+
80+
2. **Update language mapping** (if needed)
81+
- Modify `GetLanguageDisplayName()` in `LanguageAudioPlayer.cs`
82+
- Add special handling for complex language names
83+
84+
3. **Test with audio file**
85+
- Create a test MP3 file named after the language
86+
- Place it in the Audio folder
87+
- Test language switching
88+
89+
## 🐛 Bug Reports
90+
91+
When reporting bugs, please include:
92+
93+
- **Operating System**: Windows version
94+
- **Application Version**: Version number
95+
- **Steps to Reproduce**: Clear steps to reproduce the issue
96+
- **Expected Behavior**: What should happen
97+
- **Actual Behavior**: What actually happens
98+
- **Audio Files**: Information about your MP3 files (if relevant)
99+
100+
## 💡 Feature Requests
101+
102+
We welcome feature requests! Please:
103+
104+
- Check existing issues first
105+
- Describe the use case clearly
106+
- Explain why the feature would be valuable
107+
- Consider implementation complexity
108+
109+
## 🔧 Common Development Tasks
110+
111+
### Adding a New Audio Format
112+
1. Update `LanguageAudioPlayer.cs`
113+
2. Modify `FindAudioFileForLanguage()` method
114+
3. Test with the new format
115+
116+
### Improving Language Detection
117+
1. Update `GetLanguageDisplayName()` method
118+
2. Add new language mappings
119+
3. Test with various Windows language settings
120+
121+
### Enhancing System Tray
122+
1. Modify `LanguageMonitorApp.cs`
123+
2. Update `InitializeSystemTray()` method
124+
3. Test system tray interactions
125+
126+
## 📋 Pull Request Process
127+
128+
1. **Ensure your PR**:
129+
- Has a clear title and description
130+
- References any related issues
131+
- Includes appropriate tests
132+
- Follows coding standards
133+
134+
2. **PR Review Process**:
135+
- Maintainers will review your code
136+
- Address any feedback promptly
137+
- Make requested changes
138+
- Ensure CI passes
139+
140+
3. **After Approval**:
141+
- PR will be merged by maintainers
142+
- Your contribution will be credited
143+
144+
## 🏷️ Versioning
145+
146+
We use [Semantic Versioning](https://semver.org/):
147+
- **MAJOR**: Breaking changes
148+
- **MINOR**: New features (backward compatible)
149+
- **PATCH**: Bug fixes (backward compatible)
150+
151+
## 📞 Getting Help
152+
153+
- **GitHub Issues**: For bugs and feature requests
154+
- **GitHub Discussions**: For questions and general discussion
155+
- **Email**: [email protected]
156+
157+
## 🙏 Recognition
158+
159+
Contributors will be:
160+
- Listed in the project contributors
161+
- Mentioned in release notes
162+
- Credited in the README
163+
164+
Thank you for contributing to Input Language Screamer! 🎵

Desktop/Audio/Bulgarian.mp3

18 KB
Binary file not shown.

Desktop/Audio/English.mp3

11.5 KB
Binary file not shown.

Desktop/Desktop.csproj

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>WinExe</OutputType>
5+
<TargetFramework>net9.0-windows</TargetFramework>
6+
<Nullable>enable</Nullable>
7+
<UseWindowsForms>true</UseWindowsForms>
8+
<ImplicitUsings>enable</ImplicitUsings>
9+
10+
<!-- Application Metadata -->
11+
<AssemblyTitle>Input Language Screamer</AssemblyTitle>
12+
<Product>Input Language Screamer</Product>
13+
<Description>Plays language-specific MP3 audio files when Windows input language changes using Alt+Shift</Description>
14+
<Copyright>© 2025 OneBit Software</Copyright>
15+
<AssemblyVersion>1.0.0.0</AssemblyVersion>
16+
<FileVersion>1.0.0.0</FileVersion>
17+
18+
<!-- Publishing Configuration -->
19+
<PublishSingleFile>true</PublishSingleFile>
20+
<SelfContained>true</SelfContained>
21+
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
22+
<!-- Trimming disabled for Windows Forms compatibility -->
23+
<PublishTrimmed>false</PublishTrimmed>
24+
</PropertyGroup>
25+
26+
<ItemGroup>
27+
<!-- MP3 playback support -->
28+
<PackageReference Include="NAudio" Version="2.2.1" />
29+
30+
<!-- Include audio files in output -->
31+
<Content Include="Audio\*.mp3">
32+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
33+
</Content>
34+
</ItemGroup>
35+
36+
</Project>

Desktop/GlobalKeyboardHook.cs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// Input Language Screamer - Plays language-specific sounds when Windows input language changes
2+
// Global keyboard hook component for system-wide language change detection
3+
namespace OneBitSoftware.InputLanguageScreamer.Desktop;
4+
5+
using System;
6+
using System.Diagnostics;
7+
using System.Runtime.InteropServices;
8+
using System.Windows.Forms;
9+
10+
/// <summary>
11+
/// Global keyboard hook that monitors for input language changes system-wide.
12+
/// Uses Windows API low-level keyboard hook to detect Alt key releases and check for language changes.
13+
/// </summary>
14+
public class GlobalKeyboardHook
15+
{
16+
// Windows API constants for keyboard hook
17+
private const int WH_KEYBOARD_LL = 13; // Low-level keyboard input hook
18+
private const int WM_KEYUP = 0x0101; // Key up message
19+
private const int VK_LMENU = 0xA4; // Left Alt key
20+
private const int VK_RMENU = 0xA5; // Right Alt key
21+
22+
// Static fields for the hook
23+
private static LowLevelKeyboardProc _proc = HookCallback;
24+
private static IntPtr _hookID = IntPtr.Zero;
25+
private static InputLanguage? _currentLanguage;
26+
private static Action? _onLanguageChange;
27+
28+
/// <summary>
29+
/// Delegate for the low-level keyboard procedure
30+
/// </summary>
31+
public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
32+
33+
/// <summary>
34+
/// Initializes the global keyboard hook with a callback for language changes
35+
/// </summary>
36+
/// <param name="onLanguageChange">Action to execute when language changes</param>
37+
public GlobalKeyboardHook(Action onLanguageChange)
38+
{
39+
_onLanguageChange = onLanguageChange;
40+
_currentLanguage = InputLanguage.CurrentInputLanguage;
41+
_hookID = SetHook(_proc);
42+
}
43+
44+
/// <summary>
45+
/// Sets up the low-level keyboard hook using Windows API
46+
/// </summary>
47+
/// <param name="proc">The callback procedure for keyboard events</param>
48+
/// <returns>Handle to the hook</returns>
49+
private static IntPtr SetHook(LowLevelKeyboardProc proc)
50+
{
51+
using (Process curProcess = Process.GetCurrentProcess())
52+
using (ProcessModule? curModule = curProcess.MainModule)
53+
{
54+
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
55+
GetModuleHandle(curModule?.ModuleName ?? ""), 0);
56+
}
57+
}
58+
59+
/// <summary>
60+
/// Callback function that processes keyboard events from the hook
61+
/// Monitors for Alt key releases and checks for language changes
62+
/// </summary>
63+
/// <param name="nCode">Hook code</param>
64+
/// <param name="wParam">Message identifier</param>
65+
/// <param name="lParam">Pointer to keyboard input structure</param>
66+
/// <returns>Result of calling next hook</returns>
67+
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
68+
{
69+
// Only process if hook code is valid
70+
if (nCode >= 0)
71+
{
72+
// Get the virtual key code from the keyboard input structure
73+
int vkCode = Marshal.ReadInt32(lParam);
74+
75+
// Check if this is a key release event
76+
if (wParam == (IntPtr)WM_KEYUP)
77+
{
78+
// Check if Alt key was released (left or right Alt)
79+
if (vkCode == VK_LMENU || vkCode == VK_RMENU)
80+
{
81+
// Check for language change when Alt is released
82+
// This catches Alt+Shift language switching
83+
CheckLanguageChange();
84+
}
85+
}
86+
}
87+
88+
// Pass the hook information to the next hook procedure
89+
return CallNextHookEx(_hookID, nCode, wParam, lParam);
90+
}
91+
92+
/// <summary>
93+
/// Checks if the input language has changed and triggers the callback if it has
94+
/// </summary>
95+
private static void CheckLanguageChange()
96+
{
97+
var newLanguage = InputLanguage.CurrentInputLanguage;
98+
if (newLanguage != _currentLanguage)
99+
{
100+
_currentLanguage = newLanguage;
101+
_onLanguageChange?.Invoke();
102+
}
103+
}
104+
105+
/// <summary>
106+
/// Disposes of the keyboard hook resources
107+
/// </summary>
108+
public void Dispose()
109+
{
110+
UnhookWindowsHookEx(_hookID);
111+
}
112+
113+
// Windows API function imports for keyboard hook functionality
114+
115+
/// <summary>
116+
/// Installs an application-defined hook procedure into a hook chain
117+
/// </summary>
118+
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
119+
private static extern IntPtr SetWindowsHookEx(int idHook,
120+
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
121+
122+
/// <summary>
123+
/// Uninstalls a hook procedure installed by SetWindowsHookEx
124+
/// </summary>
125+
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
126+
[return: MarshalAs(UnmanagedType.Bool)]
127+
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
128+
129+
/// <summary>
130+
/// Passes hook information to the next hook procedure in the current hook chain
131+
/// </summary>
132+
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
133+
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
134+
IntPtr wParam, IntPtr lParam);
135+
136+
/// <summary>
137+
/// Retrieves a module handle for the specified module
138+
/// </summary>
139+
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
140+
private static extern IntPtr GetModuleHandle(string lpModuleName);
141+
}

0 commit comments

Comments
 (0)