First release

This commit is contained in:
Victor 2016-06-22 20:00:39 +03:00
commit aa7b7d6204
4 changed files with 383 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
target
Cargo.lock

9
Cargo.toml Normal file
View File

@ -0,0 +1,9 @@
[package]
name = "aimpremote"
version = "0.1.0"
authors = ["aNNiMON <annimon119@gmail.com>"]
[dependencies]
winapi="*"
user32-sys="*"
kernel32-sys="*"

366
src/aimpremote.rs Normal file
View File

@ -0,0 +1,366 @@
#![allow(dead_code)]
use ::{std, user32, kernel32};
use std::mem;
use std::ffi::CString;
use winapi::winuser::WM_USER;
use winapi::basetsd::INT64;
use winapi::minwindef::{BOOL, DWORD, UINT};
use winapi::memoryapi::FILE_MAP_READ;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
const AIMP_REMOTE_ACCESS_MAP_FILE_SIZE : u64 = 2048;
#[repr(C, packed)]
#[allow(non_snake_case)]
struct AIMPRemoteFileInfo {
Deprecated1: DWORD,
Active: BOOL,
BitRate: DWORD,
Channels: DWORD,
Duration: DWORD,
FileSize: INT64,
FileMark: DWORD,
SampleRate: DWORD,
TrackNumber: DWORD,
AlbumLength: DWORD,
ArtistLength: DWORD,
DateLength: DWORD,
FileNameLength: DWORD,
GenreLength: DWORD,
TitleLength: DWORD,
Deprecated2: [DWORD; 6]
}
const WM_AIMP_COMMAND : UINT = WM_USER + 0x75;
const WM_AIMP_NOTIFY : UINT = WM_USER + 0x76;
const WM_AIMP_PROPERTY : UINT = WM_USER + 0x77;
const AIMP_RA_PROPVALUE_GET : u32 = 0;
const AIMP_RA_PROPVALUE_SET : u32 = 1;
const AIMP_RA_PROPERTY_MASK : u32 = 0xFFFFFFF0;
const AIMP_RA_PROPERTY_VERSION : u32 = 0x10;
const AIMP_RA_PROPERTY_PLAYER_POSITION : u32 = 0x20;
const AIMP_RA_PROPERTY_PLAYER_DURATION : u32 = 0x30;
const AIMP_RA_PROPERTY_PLAYER_STATE : u32 = 0x40;
const AIMP_RA_PROPERTY_VOLUME : u32 = 0x50;
const AIMP_RA_PROPERTY_MUTE : u32 = 0x60;
const AIMP_RA_PROPERTY_TRACK_REPEAT : u32 = 0x70;
const AIMP_RA_PROPERTY_TRACK_SHUFFLE : u32 = 0x80;
const AIMP_RA_PROPERTY_RADIOCAP : u32 = 0x90;
const AIMP_RA_PROPERTY_VISUAL_FULLSCREEN : u32 = 0xA0;
const AIMP_RA_CMD_BASE : u32 = 10;
const AIMP_RA_CMD_PLAY : u32 = AIMP_RA_CMD_BASE + 3;
const AIMP_RA_CMD_PLAYPAUSE : u32 = AIMP_RA_CMD_BASE + 4;
const AIMP_RA_CMD_PAUSE : u32 = AIMP_RA_CMD_BASE + 5;
const AIMP_RA_CMD_STOP : u32 = AIMP_RA_CMD_BASE + 6;
const AIMP_RA_CMD_NEXT : u32 = AIMP_RA_CMD_BASE + 7;
const AIMP_RA_CMD_PREV : u32 = AIMP_RA_CMD_BASE + 8;
const AIMP_RA_CMD_VISUAL_NEXT : u32 = AIMP_RA_CMD_BASE + 9;
const AIMP_RA_CMD_VISUAL_PREV : u32 = AIMP_RA_CMD_BASE + 10;
const AIMP_RA_CMD_QUIT : u32 = AIMP_RA_CMD_BASE + 11;
const AIMP_RA_CMD_ADD_FILES : u32 = AIMP_RA_CMD_BASE + 12;
const AIMP_RA_CMD_ADD_FOLDERS : u32 = AIMP_RA_CMD_BASE + 13;
const AIMP_RA_CMD_ADD_PLAYLISTS : u32 = AIMP_RA_CMD_BASE + 14;
const AIMP_RA_CMD_ADD_URL : u32 = AIMP_RA_CMD_BASE + 15;
const AIMP_RA_CMD_OPEN_FILES : u32 = AIMP_RA_CMD_BASE + 16;
const AIMP_RA_CMD_OPEN_FOLDERS : u32 = AIMP_RA_CMD_BASE + 17;
const AIMP_RA_CMD_OPEN_PLAYLISTS : u32 = AIMP_RA_CMD_BASE + 18;
const AIMP_RA_CMD_VISUAL_START : u32 = AIMP_RA_CMD_BASE + 20;
const AIMP_RA_CMD_VISUAL_STOP : u32 = AIMP_RA_CMD_BASE + 21;
#[derive(Debug)]
pub struct TrackInfo {
pub artist: String,
pub title: String,
pub album: String,
pub genre: String,
pub filename: String,
pub date: String,
pub active: bool,
pub bitrate: u32,
pub channels: u32,
pub duration: u32,
pub filesize: i64,
pub filemark: u32,
pub sample_rate: u32,
pub track_number: u32
}
#[derive(Debug)]
pub enum State {
Stopped,
Paused,
Playing
}
#[derive(Debug)]
pub enum Dialog {
AddFiles = AIMP_RA_CMD_ADD_FILES as isize,
AddFolders = AIMP_RA_CMD_ADD_FOLDERS as isize,
AddPlaylists = AIMP_RA_CMD_ADD_PLAYLISTS as isize,
AddUrl = AIMP_RA_CMD_ADD_URL as isize,
OpenFiles = AIMP_RA_CMD_OPEN_FILES as isize,
OpenFolders = AIMP_RA_CMD_OPEN_FOLDERS as isize,
OpenPlaylists = AIMP_RA_CMD_OPEN_PLAYLISTS as isize
}
fn aimp_remote_class() -> CString {
CString::new("AIMP2_RemoteInfo").unwrap()
}
/// Returns player version: `HiWord`: Version ID (for example: 301 -> v3.01), `LoWord`: Build Number
pub fn version() -> Option<u32> {
get_property(AIMP_RA_PROPERTY_VERSION).map(|v| v as u32)
}
/// Returns player version as string
pub fn version_str() -> Option<String> {
match version() {
Some(version) => {
let hi = (version & 0xffff0000) >> 16;
let lo = version & 0xffff;
Some( format!("v{:.*}, Build {}", 2, (hi as f32) / (100 as f32), lo))
},
None => None,
}
}
/// Gets current position of now playing track (in msec)
pub fn get_position() -> Option<i64> {
get_property(AIMP_RA_PROPERTY_PLAYER_POSITION)
}
/// Sets position of now playing track (in msec)
pub fn set_position(position: i64) -> bool {
set_property(AIMP_RA_PROPERTY_PLAYER_POSITION, position)
}
/// Gets duration of now playing track (in msec)
pub fn get_duration() -> Option<i64> {
get_property(AIMP_RA_PROPERTY_PLAYER_DURATION)
}
/// Returns current player state
pub fn get_state() -> Option<State> {
match get_property(AIMP_RA_PROPERTY_PLAYER_STATE) {
Some(v) => match v {
0 => Some(State::Stopped),
1 => Some(State::Paused),
2 => Some(State::Playing),
_ => None
},
None => None
}
}
/// Sets player state
pub fn set_state(state: State) -> bool {
match state {
State::Stopped => execute_command(AIMP_RA_CMD_STOP),
State::Paused => execute_command(AIMP_RA_CMD_PAUSE),
State::Playing => execute_command(AIMP_RA_CMD_PLAY)
}
}
/// Gets current volume [0..100] (%)
pub fn get_volume() -> Option<u8> {
get_property(AIMP_RA_PROPERTY_VOLUME).map(|v| v as u8)
}
/// Sets volume [0..100] (%)
pub fn set_volume(level: u8) -> bool {
set_property(AIMP_RA_PROPERTY_VOLUME, level as i64)
}
/// Gets current mute state
pub fn is_mute() -> Option<bool> {
get_property(AIMP_RA_PROPERTY_MUTE).map(|v| v != 0)
}
/// Sets mute state
pub fn set_mute(state: bool) -> bool {
set_property(AIMP_RA_PROPERTY_MUTE, state as i64)
}
/// Gets track repeat state
pub fn is_repeat() -> Option<bool> {
get_property(AIMP_RA_PROPERTY_TRACK_REPEAT).map(|v| v != 0)
}
/// Sets track repeat state
pub fn set_repeat(state: bool) -> bool {
set_property(AIMP_RA_PROPERTY_TRACK_REPEAT, state as i64)
}
/// Gets shuffle state
pub fn is_shuffle() -> Option<bool> {
get_property(AIMP_RA_PROPERTY_TRACK_SHUFFLE).map(|v| v != 0)
}
/// Sets shuffle state
pub fn set_shuffle(state: bool) -> bool {
set_property(AIMP_RA_PROPERTY_TRACK_SHUFFLE, state as i64)
}
/// Gets radio capture state
pub fn is_radio_capture() -> Option<bool> {
get_property(AIMP_RA_PROPERTY_RADIOCAP).map(|v| v != 0)
}
/// Sets radio capture state
pub fn set_radio_capture(state: bool) -> bool {
set_property(AIMP_RA_PROPERTY_RADIOCAP, state as i64)
}
/// Gets fullscreen visualization mode
pub fn is_visualization_fullscreen() -> Option<bool> {
get_property(AIMP_RA_PROPERTY_VISUAL_FULLSCREEN).map(|v| v != 0)
}
/// Sets fullscreen visualization mode
pub fn set_visualization_fullscreen(state: bool) -> bool {
set_property(AIMP_RA_PROPERTY_VISUAL_FULLSCREEN, state as i64)
}
/// Returns current playing track info
pub fn aimp_track_info() -> Option<TrackInfo> {
let haimp = unsafe { kernel32::OpenFileMappingA(FILE_MAP_READ, 0, aimp_remote_class().as_ptr()) };
if haimp == std::ptr::null_mut() {
return None;
}
let hfilemap = unsafe {
kernel32::MapViewOfFile(haimp, FILE_MAP_READ, 0, 0, AIMP_REMOTE_ACCESS_MAP_FILE_SIZE)
};
let aimp_info = unsafe { (hfilemap as *mut AIMPRemoteFileInfo).as_ref().unwrap() };
let mut pbuff = hfilemap as *const u16;
pbuff = unsafe { pbuff.offset(mem::size_of::<AIMPRemoteFileInfo>() as isize / 2) };
let album = get_info(pbuff, aimp_info.AlbumLength as usize);
pbuff = unsafe { pbuff.offset(aimp_info.AlbumLength as isize) };
let artist = get_info(pbuff, aimp_info.ArtistLength as usize);
pbuff = unsafe { pbuff.offset(aimp_info.ArtistLength as isize) };
let date = get_info(pbuff, aimp_info.DateLength as usize);
pbuff = unsafe { pbuff.offset(aimp_info.DateLength as isize) };
let filename = get_info(pbuff, aimp_info.FileNameLength as usize);
pbuff = unsafe { pbuff.offset(aimp_info.FileNameLength as isize) };
let genre = get_info(pbuff, aimp_info.GenreLength as usize);
pbuff = unsafe { pbuff.offset(aimp_info.GenreLength as isize) };
let title = get_info(pbuff, aimp_info.TitleLength as usize);
let result = TrackInfo {
artist: artist,
title: title,
album: album,
genre: genre,
filename: filename,
date: date,
active: aimp_info.Active != 0,
bitrate: aimp_info.BitRate,
channels: aimp_info.Channels,
duration: aimp_info.Duration,
filesize: aimp_info.FileSize,
filemark: aimp_info.FileMark,
sample_rate: aimp_info.SampleRate,
track_number: aimp_info.TrackNumber
};
unsafe { kernel32::UnmapViewOfFile(hfilemap) };
Some(result)
}
/// Sets player state to playing if paused, and paused if playing
pub fn play_pause() -> bool {
execute_command(AIMP_RA_CMD_PLAYPAUSE)
}
/// Switches to next track
pub fn next_track() -> bool {
execute_command(AIMP_RA_CMD_NEXT)
}
/// Switches to previous track
pub fn prev_track() -> bool {
execute_command(AIMP_RA_CMD_PREV)
}
/// Switches to next visualization
pub fn next_visualization() -> bool {
execute_command(AIMP_RA_CMD_VISUAL_NEXT)
}
/// Switches to previous visualization
pub fn prev_visualization() -> bool {
execute_command(AIMP_RA_CMD_VISUAL_PREV)
}
/// Shows dialog
pub fn show_dialog(dialog: Dialog) -> bool {
execute_command(dialog as u32)
}
/// Starts first visualization
pub fn visualization_start() -> bool {
execute_command(AIMP_RA_CMD_VISUAL_START)
}
/// Stops visualization
pub fn visualization_stop() -> bool {
execute_command(AIMP_RA_CMD_VISUAL_STOP)
}
/// Closes the program
pub fn quit() -> bool {
execute_command(AIMP_RA_CMD_QUIT)
}
fn get_property(property: u32) -> Option<i64> {
let hwnd = unsafe { user32::FindWindowA(aimp_remote_class().as_ptr(), std::ptr::null_mut()) };
if hwnd == std::ptr::null_mut() {
None
} else {
let result = unsafe {
user32::SendMessageA(hwnd, WM_AIMP_PROPERTY, (property | AIMP_RA_PROPVALUE_GET) as u64, 0)
};
Some(result)
}
}
fn set_property(property: u32, value: i64) -> bool {
let hwnd = unsafe { user32::FindWindowA(aimp_remote_class().as_ptr(), std::ptr::null_mut()) };
if hwnd == std::ptr::null_mut() {
false
} else {
unsafe {
user32::SendMessageA(hwnd, WM_AIMP_PROPERTY, (property | AIMP_RA_PROPVALUE_SET) as u64, value)
};
true
}
}
fn execute_command(command: u32) -> bool {
let hwnd = unsafe { user32::FindWindowA(aimp_remote_class().as_ptr(), std::ptr::null_mut()) };
if hwnd == std::ptr::null_mut() {
false
} else {
unsafe {
user32::SendMessageA(hwnd, WM_AIMP_COMMAND, command as u64, 0)
};
true
}
}
fn get_info(ptr: *const u16, length: usize) -> String {
unsafe {
assert!(!ptr.is_null());
let slice = std::slice::from_raw_parts(ptr, length);
OsString::from_wide(slice).to_string_lossy().into_owned()
}
}

6
src/lib.rs Normal file
View File

@ -0,0 +1,6 @@
extern crate winapi;
extern crate user32;
extern crate kernel32;
pub mod aimpremote;
pub use aimpremote::*;