hexchat_api/hexchat.rs
1//! This file holds the Hexchat struct used to interface with Hexchat. Its
2//! fields are the actual callbacks provided by Hexchat. When Hexchat
3//! loads this library, the Hexchat pointer is stored and used by casting it
4//! to the struct contained in this file. These native function pointers are
5//! private to this crate, and a more Rust-friendly API is provided through
6//! this Hexchat interface.
7
8use libc::{c_int, c_char, c_void, time_t};
9use std::ptr;
10use std::ffi::CString;
11use std::fmt;
12use std::fmt::Debug;
13use std::ops::FnMut;
14use std::ptr::null;
15use std::str;
16use enumflags2::{bitflags, BitFlags};
17
18use crate::callback_data::{CallbackData, TimerCallbackOnce};
19use crate::context::Context;
20use crate::{hexchat_callbacks::*, HexchatError};
21#[cfg(feature = "threadsafe")]
22use crate::hexchat_entry_points::PHEXCHAT;
23use crate::hook::Hook;
24use crate::list_iterator::ListIterator;
25use crate::plugin::Plugin;
26use crate::user_data::*;
27use crate::utils::*;
28#[cfg(feature = "threadsafe")]
29use crate::threadsafe_hexchat::*;
30
31/// Value used in example from the Hexchat Plugin Interface doc web page.
32const MAX_PREF_VALUE_SIZE: usize = 512;
33
34/// Value specified on the [Hexchat Plugin Interface web page]
35/// (https://hexchat.readthedocs.io/en/latest/plugins.html).
36const MAX_PREF_LIST_SIZE : usize = 4096;
37
38// hexchat_send_modes, hexchat_event_attrs_free, pluginpref_delete,
39
40/// The priorty for a given callback invoked by Hexchat.
41pub enum Priority {
42 Highest = 127,
43 High = 64,
44 Norm = 0,
45 Low = -64,
46 Lowest = -128,
47}
48
49/// The return value for client plugin callbacks.
50pub enum Eat {
51 None = 0,
52 Hexchat = 1,
53 Plugin = 2,
54 All = 3,
55}
56
57/// File descriptor types.
58#[bitflags]
59#[repr(u32)]
60#[derive(Copy, Clone, Debug, PartialEq)]
61pub enum FD {
62 Read = 1,
63 Write = 2,
64 Exception = 4,
65 NotSocket = 8,
66}
67
68/// Used by the `hexthat.strip()` function to determine what to strip from the
69/// target string.
70pub enum StripFlags {
71 StripMIrcColors = 1,
72 StripTextAttributes = 2,
73 StripBoth = 3,
74}
75
76/// This is the rust-facing Hexchat API. Each method has a corresponding
77/// C function pointer which they wrap and marshal data/from.
78///
79impl Hexchat {
80
81 /// Returns a thread-safe wrapper for `Hexchat` that exposes thread-safe
82 /// methods wrapping several of `Hexchat`s methods.
83 ///
84 #[cfg(feature = "threadsafe")]
85 pub fn threadsafe(&self) -> ThreadSafeHexchat {
86 ThreadSafeHexchat::new(unsafe { &*PHEXCHAT })
87 }
88
89 /// Prints the string passed to it to the active Hexchat window.
90 /// # Arguments
91 /// * `text` - The text to print.
92 ///
93 pub fn print(&self, text: &str) {
94 let text = str2cstring(text);
95 unsafe { (self.c_print)(self, text.as_ptr()); }
96 }
97
98 /// Invokes the Hexchat command specified by `command`.
99 /// # Arguments
100 /// * `command` - The Hexchat command to invoke.
101 ///
102 pub fn command(&self, command: &str) {
103 let command = str2cstring(command);
104 unsafe { (self.c_command)(self, command.as_ptr()); }
105 }
106
107 /// Registeres a command callback with Hexchat. This will add a user
108 /// invocable slash "/" command that can be seen when listing `/help`.
109 /// The callback can be a static function, or a closure, that has the form:
110 /// ``` no_test
111 /// FnMut(&Hexchat, &[String], &[String], &UserData) -> Eat
112 /// ```
113 /// Note that the callback parameters include a reference to the `Hexchat`
114 /// object as a convenience. This differs from the C interface which doesn't
115 /// include it.
116 ///
117 /// # Arguments
118 /// * `name` - The name of the event that invokes the callback.
119 /// * `pri` - The priority of the callback.
120 /// * `callback` - The static function or closure to register.
121 /// * `help` - Help text displayed by Hexchat for the command.
122 /// * `user_data` - Data passed back to the callback when invoked.
123 ///
124 /// # Returns
125 /// A `Hook` object associated with the callback.
126 ///
127 pub fn hook_command<F>(&self,
128 name : &str,
129 pri : Priority,
130 callback : F,
131 help : &str,
132 user_data : UserData)
133 -> Hook
134 where
135 F: FnMut(&Hexchat, &[String], &[String], &UserData)
136 -> Eat + 'static
137 {
138 let hook = Hook::new();
139 let ud = Box::new(
140 CallbackData::new_command_data(
141 Box::new(callback),
142 user_data,
143 hook.clone()));
144
145 let ud = Box::into_raw(ud) as *mut c_void;
146
147 hook.set_cbd(ud);
148
149 let help = if !help.is_empty() {
150 help
151 } else {
152 "No help available for this command."
153 };
154 let name = str2cstring(name);
155 let help = str2cstring(help);
156 unsafe {
157 // TODO - Consider making an empty help string cause a NULL to be
158 // used as hook_command()'s 5th argument.
159 hook.set((self.c_hook_command)(self,
160 name.as_ptr(),
161 pri as i32,
162 c_callback,
163 help.as_ptr(),
164 ud));
165 }
166 hook
167 }
168
169 /// Registers a callback to be called when a certain server event occurs.
170 /// For any of these functions, more information can be found at
171 /// [Hexchat Plugin Interface](https://hexchat.readthedocs.io/en/latest/plugins.html)
172 /// The callback needs to be compatible with this signature:
173 /// ``` no_test
174 /// FnMut(&Hexchat, &[String], &[String], &UserData) -> Eat
175 /// ```
176 /// # Arguments
177 /// * `name` - The name of the event to listen for.
178 /// * `pri` - The priority of the callback.
179 /// * `callback` - The callback to invoke when the event occurs.
180 /// * `user_data` - The user data that gets passed back to the callback
181 /// when it's invoked.
182 /// # Returns
183 /// * A `Hook` object that can be used to deregister the callback. It
184 /// doesn't need to be retained if not needed.
185 ///
186 pub fn hook_server<F>(&self,
187 name : &str,
188 pri : Priority,
189 callback : F,
190 user_data : UserData)
191 -> Hook
192 where
193 F: FnMut(&Hexchat, &[String], &[String], &UserData)
194 -> Eat + 'static
195 {
196 let hook = Hook::new();
197 let ud = Box::new(
198 CallbackData::new_command_data(
199 Box::new(callback),
200 user_data,
201 hook.clone()
202 ));
203 let ud = Box::into_raw(ud) as *mut c_void;
204
205 hook.set_cbd(ud);
206 let name = str2cstring(name);
207 unsafe {
208 hook.set((self.c_hook_server)(self,
209 name.as_ptr(),
210 pri as i32,
211 c_callback,
212 ud));
213 }
214 hook
215 }
216
217 /// Registers a callback to be called when a given print event occurs. This
218 /// can be any of the text events listed under Settings > Text Events.
219 /// Callback needs to be compatible with this signature:
220 /// ``` no_test
221 /// FnMut(&Hexchat, &[String], &UserData) -> Eat
222 /// ```
223 /// # Arguments
224 /// * `name` - The name of the event to listen for.
225 /// * `pri` - The priority of the callback.
226 /// * `callback` - The callback to invoke when the event occurs.
227 /// * `user_data` - The user data that gets passed back to the callback
228 /// when it's invoked.
229 /// # Returns
230 /// * A `Hook` object that can be used to deregister the callback. It
231 /// doesn't need to be retained if not needed.
232 ///
233 pub fn hook_print<F>(&self,
234 event_name : &str,
235 pri : Priority,
236 callback : F,
237 user_data : UserData)
238 -> Hook
239 where
240 F: FnMut(&Hexchat, &[String], &UserData) -> Eat + 'static
241 {
242 let hook = Hook::new();
243 let ud = Box::new(
244 CallbackData::new_print_data(
245 Box::new(callback),
246 user_data,
247 hook.clone()
248 ));
249 let ud = Box::into_raw(ud) as *mut c_void;
250
251 hook.set_cbd(ud);
252 let event_name = str2cstring(event_name);
253 unsafe {
254 hook.set((self.c_hook_print)(self,
255 event_name.as_ptr(),
256 pri as i32,
257 c_print_callback,
258 ud));
259 }
260 hook
261 }
262
263 /// Registers a callback to be called when a given print event occurs.
264 /// The callback will be invoked with an `EventAttrs` object containing
265 /// a `time_t` value for the event. The callback needs to be compatible
266 /// with this signature:
267 /// ``` no_test
268 /// FnMut(&Hexchat, &[String], &EventAttrs, &UserData) -> Eat
269 /// ```
270 /// # Arguments
271 /// * `name` - The name of the event to listen for.
272 /// * `pri` - The priority of the callback.
273 /// * `callback` - The callback to invoke when the event occurs.
274 /// * `user_data` - The user data that gets passed back to the callback
275 /// when it's invoked.
276 /// # Returns
277 /// * A `Hook` object that can be used to deregister the callback. It
278 /// doesn't need to be retained if not needed.
279 ///
280 pub fn hook_print_attrs<F>(&self,
281 name : &str,
282 pri : Priority,
283 callback : F,
284 user_data : UserData)
285 -> Hook
286 where
287 F: FnMut(&Hexchat, &[String], &EventAttrs, &UserData)
288 -> Eat + 'static
289 {
290 let hook = Hook::new();
291 let ud = Box::new(
292 CallbackData::new_print_attrs_data(
293 Box::new(callback),
294 user_data,
295 hook.clone()
296 ));
297 let ud = Box::into_raw(ud) as *mut c_void;
298
299 hook.set_cbd(ud);
300 let name = str2cstring(name);
301 unsafe {
302 hook.set((self.c_hook_print_attrs)(self,
303 name.as_ptr(),
304 pri as i32,
305 c_print_attrs_callback,
306 ud));
307 }
308 hook
309 }
310
311
312 /// Sets up a callback to be invoked every `timeout` milliseconds. The
313 /// callback needs to be compatible with:
314 /// ``` no_test
315 /// FnMut(&Hexchat, &UserData) -> i32
316 /// ```
317 /// # Arguments
318 /// * `timeout` - The timeout in milliseconds.
319 /// * `callback` - The `FnOnce()` callback.
320 /// * `user_data` - User data included with the callback and passed back
321 /// to the callback during invocation.
322 /// # Returns
323 /// * A `Hook` object that is can be used to deregister the callback.
324 ///
325 pub fn hook_timer<F>(&self,
326 timeout : i64,
327 callback : F,
328 user_data : UserData)
329 -> Hook
330 where
331 F: FnMut(&Hexchat, &UserData) -> i32 + 'static
332 {
333 let hook = Hook::new();
334 let ud = Box::new(CallbackData::new_timer_data(
335 Box::new(callback),
336 user_data,
337 hook.clone()
338 ));
339 let ud = Box::into_raw(ud) as *mut c_void;
340
341 hook.set_cbd(ud);
342
343 unsafe {
344 hook.set((self.c_hook_timer)(self,
345 timeout as c_int,
346 c_timer_callback,
347 ud));
348 }
349 hook
350 }
351
352 /// This is a special case feature, used internally to enable other threads
353 /// to invoke callbacks on the main thread. This function isn't exported
354 /// with the rest of the functions of this class.
355 /// # Arguments
356 /// * `timeout` - The timeout in milliseconds.
357 /// * `callback` - The `FnOnce()` callback.
358 /// * `user_data` - User data included with the callback and passed back
359 /// to the callback during invocation.
360 /// # Returns
361 /// * A `Hook` object that is used to deregister the callback after it's
362 /// invoked.
363 ///
364 #[allow(dead_code)]
365 pub (crate)
366 fn hook_timer_once(&self,
367 timeout : i64,
368 callback : Box<TimerCallbackOnce>,
369 user_data : UserData)
370 -> Hook
371 {
372 // TODO - Put the function signatures somewhere logical (?)
373
374 let hook = Hook::new();
375 let ud = Box::new(CallbackData::new_timer_once_data(
376 callback,
377 user_data,
378 hook.clone()
379 ));
380 let ud = Box::into_raw(ud) as *mut c_void;
381
382 hook.set_cbd(ud);
383
384 unsafe {
385 hook.set((self.c_hook_timer)(self,
386 timeout as c_int,
387 c_timer_callback_once,
388 ud));
389 }
390 hook
391 }
392 /// Hooks a socket or file descriptor. WIN32: Passing a pipe from MSVCR71,
393 /// MSVCR80 or other variations is not supported at this time.
394 ///
395 pub fn hook_fd<F>(&self,
396 fd : i32,
397 flags : BitFlags<FD>,
398 callback : F,
399 user_data : UserData)
400 -> Hook
401 where
402 F: FnMut(&Hexchat, i32, BitFlags<FD>, &UserData) -> Eat + 'static
403 {
404 let hook = Hook::new();
405 let ud = Box::new(CallbackData::new_fd_data(
406 Box::new(callback),
407 user_data,
408 hook.clone()
409 ));
410 let ud = Box::into_raw(ud) as *mut c_void;
411
412 hook.set_cbd(ud);
413
414 unsafe {
415 hook.set((self.c_hook_fd)(self,
416 fd as c_int,
417 flags.bits() as c_int,
418 c_fd_callback,
419 ud));
420 }
421 hook
422 }
423
424 /// Unhooks any Hook that was returned from a callback registration.
425 /// Ownership of the user_data is transferred to the caller.
426 /// Note: Hexchat unhooks all hooks automatically when a plugin is unloaded,
427 /// so the client plugin doesn't have to in that case.
428 /// # Arguments
429 /// * `hook` - The callback hook to deregister with Hexchat.
430 /// # Returns
431 /// * The user data that was registered with the callback using one of the
432 /// hook commands. Ownership of this object is transferred to the caller.
433 ///
434 pub fn unhook(&self, hook: &mut Hook) -> UserData {
435 hook.unhook()
436 }
437
438
439 /// Issues one of the Hexchat IRC events. The command works for any of the
440 /// events listed in Settings > Text Events dialog.
441 /// # Arguments
442 /// * `event_name` - The name of the Hexchat text event to send.
443 /// * `var_args` - A slice of `&str`'s containing the event's arguments.
444 /// # Returns
445 /// * On success, `Ok(())` is returned; otherwise, `Err(<HexchatError>)`.
446 ///
447 pub fn emit_print(&self, event_name: &str, var_args: &[&str])
448 -> Result<(), HexchatError>
449 {
450 let event_attrs = EventAttrs { server_time_utc: 0 };
451 self.emit_print_impl(0, &event_attrs, event_name, var_args)
452 }
453
454
455 /// Issues one of the Hexchat IRC events. The command works for any of the
456 /// events listed in Settings > Text Events dialog.
457 /// # Arguments
458 /// * `event_attrs` - A reference to an `EventAttrs` struct.
459 /// * `event_name` - The name of the Hexchat text event to send.
460 /// * `var_args` - A slice of `&str`'s containing the event's arguments.
461 /// # Returns
462 /// * On success, `Ok(())` is returned; otherwise, `Err(<HexchatError>)`.
463 ///
464 pub fn emit_print_attrs(&self,
465 event_attrs : &EventAttrs,
466 event_name : &str,
467 var_args : &[&str])
468 -> Result<(), HexchatError>
469 {
470 self.emit_print_impl(1, event_attrs, event_name, var_args)
471 }
472
473 /// Issues one of the Hexchat IRC events. Called internally by the public
474 /// commands, `emit_print()` and `emit_print_attrs()`. The command works
475 /// for any of the events listed in Settings > Text Events dialog.
476 /// # Arguments
477 /// * `ver` - 0 to invoke `hc.c_emit_print()`, 1 to invoke
478 /// `hc.c_emit_print_attrs()`.
479 /// * `event_attrs` - A reference to an `EventAttrs` struct.
480 /// * `event_name` - The name of the Hexchat text event to send.
481 /// * `var_args` - A slice of `&str`'s containing the event's arguments.
482 /// # Returns
483 /// * On success, `Ok(())` is returned; otherwise, `Err(<HexchatError>)`.
484 ///
485 fn emit_print_impl(&self,
486 ver : i32,
487 event_attrs : &EventAttrs,
488 event_name : &str,
489 var_args : &[&str])
490 -> Result<(), HexchatError>
491 {
492 let mut args = vec![];
493 let name = str2cstring(event_name);
494 let va_len = var_args.len();
495
496 // We don't know if there are 6 items in var_args - so Clippy's
497 // suggestion would fail. This range loop is fine.
498 #[allow(clippy::needless_range_loop)]
499 for i in 0..6 {
500 args.push(str2cstring(if i < va_len { var_args[i] } else { "" }));
501 }
502
503 // TODO - If empty strings don't suffice as a nop param, then construct
504 // another vector containing pointers and pad with nulls.
505 unsafe {
506 use HexchatError::*;
507
508 if ver == 0 {
509 let result = (self.c_emit_print)(
510 self,
511 name.as_ptr(), args[0].as_ptr(),
512 args[1].as_ptr(), args[2].as_ptr(),
513 args[3].as_ptr(), args[4].as_ptr(),
514 args[5].as_ptr(), null::<c_char>());
515 if result > 0 {
516 Ok(())
517 } else {
518 Err(CommandFailed(format!("`.emit_print(\"{}\", {:?})` \
519 failed. Check the event name \
520 and data for errors.",
521 event_name, var_args)))
522 }
523 } else {
524 let result = (self.c_emit_print_attrs)(
525 self, event_attrs,
526 name.as_ptr(), args[0].as_ptr(),
527 args[1].as_ptr(), args[2].as_ptr(),
528 args[3].as_ptr(), args[4].as_ptr(),
529 args[5].as_ptr(), null::<c_char>());
530 if result > 0 {
531 Ok(())
532 } else {
533 Err(CommandFailed(format!(
534 "`.emit_print_attrs(\"{}\", {:?})` \
535 failed. Check the event name \
536 and data for errors.",
537 event_name, var_args)))
538 }
539 }
540 }
541 }
542
543 /// Compares two nicknames, returning a similar value to `strcmp()`.
544 /// If they're equal (0), s1 < s2 (<0 - negative), or s1 > s2 (>0 positive).
545 /// # Arguments
546 /// * `s1` - The first nickname to compare.
547 /// * `s2` - The second.
548 /// # Returns
549 /// * If the first non-matching character is of lesser value for `s1`, a
550 /// negative value is returned; if `s1`'s char is greater, then a non-0
551 /// postive value is returned. 0 is returned if they match.
552 ///
553 pub fn nickcmp(&self, s1: &str, s2: &str) -> i32 {
554 let s1 = str2cstring(s1);
555 let s2 = str2cstring(s2);
556 unsafe {
557 (self.c_nickcmp)(self, s1.as_ptr(), s2.as_ptr())
558 }
559 }
560
561 /// Converts a string with text attributes and IRC colors embedded into
562 /// a plain text string. Either IRC colors, or text attributes (or both)
563 /// can be stripped out of the string.
564 /// # Arguments
565 /// * `text` - The string to strip.
566 /// * `flags` - One of the `StripFlags` cases (`StripMIrcColors`,
567 /// `StripTextAttributes`, `StripBoth`).
568 /// # Returns
569 /// * `Some(<stripped-string>)` or `None` if the operation failed.
570 ///
571 pub fn strip(&self, text: &str, flags: StripFlags) -> Option<String> {
572 let length = text.len() as i32;
573 let text = str2cstring(text);
574 let result = unsafe {
575 (self.c_strip)(self, text.as_ptr(), length, flags as i32)
576 };
577 if !result.is_null() {
578 let stripped = pchar2string(result);
579 unsafe { (self.c_free)(self, result as *const c_void); }
580 Some(stripped)
581 } else { None }
582 }
583
584 /// Sets the currently active context to that bound to the `Context`
585 /// object. The contexts are essentially the channels the user is in
586 /// and has open tabs/windows to them. The `Context` object itself has
587 /// a `.set()` method that can be invoked directly, which this command
588 /// invokes.
589 /// # Arguments
590 /// * `context` - The `Context` to make the currently active context.
591 /// # Returns
592 /// * A result (`Result<(), HexchatError>) where `Ok(())` indicates
593 /// the context has been switched, and a `HexchatError` if it didn't.
594 ///
595 pub fn set_context(&self, context: &Context) -> Result<(), HexchatError> {
596 context.set()
597 }
598
599 /// Returns a `Context` object bound to the requested server/channel.
600 /// The object provides methods like `print()` that will execute the
601 /// Hexchat print command in that tab/window related to the context.
602 /// The `Context::find()` can also be invoked to find a context.
603 /// # Arguments
604 /// * `network` - The network (e.g. "freenode") of the context.
605 /// * `channel` - The channel name for the context (e.g. "##rust").
606 /// # Returns
607 /// * the context was found, i.e. if the user is joined to the channel
608 /// specified currently, a `Some(<Context>)` is returned with the
609 /// context object; `None` otherwise.
610 ///
611 pub fn find_context(&self, network: &str, channel: &str)
612 -> Option<Context>
613 {
614 Context::find(network, channel)
615 }
616
617 /// Returns a `Context` object for the current context (Hexchat tab/window
618 /// currently visible in the app). This object can be used to invoke
619 /// the Hexchat API within the context the object is bound to. Also,
620 /// `Context::get()` will return a context object for the current context.
621 /// # Returns
622 /// * The `Context` for the currently active context. This usually means
623 /// the channel window the user has visible in the GUI.
624 ///
625 pub fn get_context(&self) -> Option<Context> {
626 Context::get()
627 }
628
629 /// Retrieves the info data with the given `id`. It returns None on failure
630 /// and `Some(String)` on success. All information is returned as String
631 /// data - even the "win_ptr"/"gtkwin_ptr" values, which can be parsed
632 /// and cast to pointers.
633 /// # Arguments
634 /// * `id` - The name/identifier for the information needed. A list of
635 /// the names for some of these can be found on the Hexchat
636 /// Plugin Interface page under `hexchat_get_info()`. These include
637 /// "channel", "network", "topic", etc.
638 /// # Returns
639 /// * `Some(<String>)` is returned with the string value of the info
640 /// requested. `None` is returned if there is no info with the requested
641 /// `id`.
642 ///
643 pub fn get_info(&self, id: &str) -> Option<String> {
644 let idstr = str2cstring(id);
645 let info = unsafe { (self.c_get_info)(self, idstr.as_ptr()) };
646 if !info.is_null() {
647 match id {
648 "win_ptr" | "gtkwin_ptr" => {
649 Some((info as u64).to_string())
650 },
651 _ => {
652 Some(pchar2string(info))
653 },
654 }
655 } else { None }
656 }
657
658 /// Returns the requested pref value, or None if it doesn't exist. These
659 /// are settings specific to Hexchat itself. It's possible to get the
660 /// user's input box text cursor position via this command with
661 /// "state_cursor", for instance. Other preferences can be listed with the
662 /// `/set` command.
663 /// # Arguments
664 /// * name - The name of the pref to read.
665 /// # Returns
666 /// * `Some(PrefValue)` if the pref exists, `None` otherwise.
667 ///
668 pub fn get_prefs(&self, name: &str) -> Option<PrefValue> {
669 let namecstr = str2cstring(name);
670 unsafe {
671 let mut str_ptr: *const c_char = ptr::null();
672 let mut int_loc: c_int = 0;
673 let result = (self.c_get_prefs)(self,
674 namecstr.as_ptr(),
675 &mut str_ptr,
676 &mut int_loc);
677 match result {
678 1 => { Some(StringVal(pchar2string(str_ptr))) },
679 2 => { Some(IntegerVal(int_loc as i32)) },
680 3 => { Some(BoolVal( int_loc != 0 )) },
681 _ => { None },
682 }
683 }
684 }
685
686 /// Creates an iterator for the requested Hexchat list. This is modeled
687 /// after how Hexchat implements the listing feature: rather than load
688 /// all the list items up front, an internal list pointer is advanced
689 /// to the current item, and the fields of which are accessible through
690 /// the iterator's `.get_field()` function. List iterators can also be
691 /// created by invoking `ListIterator::new(<list-name>)`. See the Hexchat
692 /// Plugin Interface web page for more information on the related lists.
693 /// # Arguments
694 /// * `name` - The name of the list to iterate over.
695 /// # Returns
696 /// * If the list exists, `Some(ListIterator)` is returned; `None`
697 /// otherwise.
698 ///
699 pub fn list_get(&self, name: &str) -> Option<ListIterator> {
700 ListIterator::new(name)
701 }
702
703 /// Writes a variable name and value to a configuration file maintained
704 /// by Hexchat for your plugin. These can be accessed later using
705 /// `pluginpref_get()`. *A character representing the type of the pref is
706 /// prepended to the value output to the config file. `pluginpref_get()`
707 /// uses this when reading back values from the config file to return the
708 /// correct variant of `PrefValue`.*
709 /// # Arguments
710 /// * `name` - The name of the pref to set.
711 /// * `value` - The value to set - an instance of one of the `PrefValue`
712 /// types (`StringVal, IntVal, or BoolVal`).
713 /// # Returns
714 /// * `true` if the operation succeeds, `false` otherwise.
715 ///
716 pub fn pluginpref_set(&self, name: &str, value: PrefValue) -> bool {
717 let sval = value.simple_ser();
718 let namecstr = str2cstring(name);
719 let svalcstr = CString::new(sval.clone()).unwrap();
720 if sval.len() > MAX_PREF_VALUE_SIZE {
721 panic!("`hexchat.pluginpref_set({}, <overflow>)`: the value \
722 exceeds the max allowable size of {:?} bytes.",
723 name, MAX_PREF_VALUE_SIZE);
724 }
725 unsafe {
726 (self.c_pluginpref_set_str)(self,
727 namecstr.as_ptr(),
728 svalcstr.as_ptr()) > 0
729 }
730 }
731
732 /// Retrieves, from a config file that Hexchat manages for your plugin,
733 /// the value for the named variable that had been previously created using
734 /// `pluginpref_set()`.
735 /// # Arguments
736 /// * `name` - The name of the pref to load.
737 /// # Returns
738 /// * `Some(<PrefValue>)` holding the value of the requested pref if it
739 /// exists, `None` otherwise.
740 ///
741 pub fn pluginpref_get(&self, name: &str) -> Option<PrefValue> {
742 let mut buf = [0i8; MAX_PREF_VALUE_SIZE];
743 let name = str2cstring(name);
744 if unsafe { (self.c_pluginpref_get_str)(self,
745 name.as_ptr(),
746 buf.as_mut_ptr()) > 0 }
747 {
748 let sval = pchar2string(buf.as_ptr());
749 Some(PrefValue::simple_deser(&sval))
750 } else { None }
751 }
752
753 /// Returns a list of all the plugin pref variable names your plugin
754 /// registered using `pluginpref_set()`. `pluginpref_get()` can be invoked
755 /// with each item to get their values.
756 /// # Returns
757 /// * `Some(Vec<String>)` if prefs exist for the plugin, `None` otherwise.
758 /// The vector contains the names of the prefs registered.
759 ///
760 pub fn pluginpref_list(&self) -> Option<Vec<String>> {
761 let mut buf = [0i8; MAX_PREF_LIST_SIZE];
762 if unsafe { (self.c_pluginpref_list)(self, buf.as_mut_ptr()) > 0 } {
763 let s = pchar2string(buf.as_ptr());
764 if !s.is_empty() {
765 let mut v = vec![];
766 for name in s.split(',') {
767 if !name.is_empty() {
768 v.push(name.to_string());
769 }
770 }
771 Some(v)
772 } else { None }
773 } else { None }
774 }
775
776 /// Adds a dummy entry in Hexchat's list of plugins. The "plugin" registered
777 /// using this command is visible in the "Plugins and Scripts" dialog and
778 /// using other slash "/" commands; however, that's all it does. This
779 /// command is useful when embedding a script interpreter that loads
780 /// scripts as plugin code. Each script thus loaded can be visible to the
781 /// user as a plugin. If writing a native plugin, you don't need to be
782 /// concerned with this command as your plugin's info is registered during
783 /// init from the `PluginInfo` object provided by your `plugin_get_info()`
784 /// function.
785 /// # Arguments
786 /// * `filename` - This can be the name of a script or binary.
787 /// * `name` - The name of the plugin.
788 /// * `desc` - The description of the plugin.
789 /// * `version` - A version string.
790 /// # Returns
791 /// * A new `Plugin` object that represents the plugin entry in Hexchat.
792 /// It can be used to deregister the plugin, and it (or a clone of it)
793 /// needs to be retained; otherwise, the plugin entry will be removed
794 /// when the last copy of the `Plugin` object goes out of scope.
795 ///
796 pub fn plugingui_add(&self,
797 filename : &str,
798 name : &str,
799 desc : &str,
800 version : &str)
801 -> Plugin
802 {
803 Plugin::new(filename, name, desc, version)
804 }
805
806 /// Removes the dummy plugin entry from the Hexchat environment. The
807 /// dummy plugin would have been registered using `hexchat.plugingui_add()`.
808 ///
809 pub fn plugingui_remove(&self, plugin: &Plugin) {
810 plugin.remove();
811 }
812}
813
814/// Represents the values that can be accessed using the prefs functions of
815/// the `Hexchat` object (`hc.pluginpref_get()`, `hc.pluginpref_get()`, etc.).
816/// The enumeration enables the typing of the values stored and retrieved.
817///
818#[derive(Debug)]
819pub enum PrefValue {
820 StringVal(String),
821 IntegerVal(i32),
822 BoolVal(bool),
823}
824use PrefValue::*;
825
826impl PrefValue {
827 pub fn str(&self) -> String {
828 match self {
829 StringVal(s) => { s.clone() },
830 IntegerVal(i) => { i.to_string() },
831 BoolVal(b) => { b.to_string() },
832 }
833 }
834 pub fn int(&self) -> i32 {
835 match self {
836 StringVal(s) => {
837 s.parse::<i32>().unwrap_or(0)
838 },
839 IntegerVal(i) => { *i },
840 BoolVal(b) => { if *b { 1 } else { 0 } },
841 }
842 }
843
844 pub fn bool(&self) -> bool {
845 match self {
846 StringVal(s) => {
847 s.parse::<bool>().unwrap_or(false)
848 },
849 IntegerVal(i) => { *i != 0 },
850 BoolVal(b) => { *b },
851 }
852 }
853
854 /// Simple config file value serialization into string.
855 /// The string produced can be written to the config file Hexchat maintains.
856 /// A type character is prepended ('s', 'i', or 'b').
857 ///
858 fn simple_ser(&self) -> String {
859 match self {
860 StringVal(s) => {
861 let mut sstr = s.clone();
862 sstr.insert(0, 's');
863 sstr
864 },
865 IntegerVal(i) => {
866 let mut istr = i.to_string();
867 istr.insert(0, 'i');
868 istr
869 },
870 BoolVal(b) => {
871 let mut bstr = b.to_string();
872 bstr.insert(0, 'b');
873 bstr
874 },
875 }
876 }
877 /// Simple config file value deserialization from a string to a `PrefValue`.
878 /// Treats the first character of the string read in from the config file
879 /// as the type, which it then discards and parses the rest of the string
880 /// to return the correct variant of `PrefValue`.
881 ///
882 fn simple_deser(s: &str) -> PrefValue {
883 if s.len() > 1 {
884 match &s[0..1] {
885 "s" => {
886 StringVal(s.to_string())
887 },
888 "i" => {
889 if let Ok(v) = s[1..].parse::<i32>() {
890 IntegerVal(v)
891 } else { StringVal(s.to_string()) }
892 },
893 "b" => {
894 if let Ok(v) = s[1..].parse::<bool>() {
895 BoolVal(v)
896 } else { StringVal(s.to_string()) }
897 },
898 _ => { StringVal(s.to_string()) },
899 }
900 } else {
901 StringVal(s.to_string())
902 }
903 }
904}
905
906impl From<PrefValue> for String {
907 fn from(pv: PrefValue) -> String {
908 pv.str()
909 }
910}
911
912impl From<PrefValue> for i32 {
913 fn from(pv: PrefValue) -> i32 {
914 pv.int()
915 }
916}
917
918impl From<PrefValue> for bool {
919 fn from(pv: PrefValue) -> bool {
920 pv.bool()
921 }
922}
923
924impl From<String> for PrefValue {
925 fn from(s: String) -> PrefValue {
926 StringVal(s)
927 }
928}
929
930impl From<i32> for PrefValue {
931 fn from(i: i32) -> PrefValue {
932 IntegerVal(i)
933 }
934}
935
936impl From<bool> for PrefValue {
937 fn from(b: bool) -> PrefValue {
938 BoolVal(b)
939 }
940}
941
942// Apply the same attribute to all items in the block, but don't compile it as
943// an actual block.
944macro_rules! apply_attrib {
945 (#![$attr:meta] $( $it:item )*) => { $( #[$attr] $it )* }
946}
947
948apply_attrib! {
949#![allow(non_camel_case_types)]
950
951/// Some types used by the C struct below.
952pub (crate) type hexchat_hook = c_void;
953pub (crate) type hexchat_list = c_void;
954pub (crate) type hexchat_context = c_void;
955#[allow(dead_code)]
956pub (crate) type hexchat_event_attrs = c_void;
957
958/// Mirrors the callback function pointer of Hexchat.
959type C_Callback = extern "C"
960 fn(word : *const *const c_char,
961 word_eol : *const *const c_char,
962 user_data : *mut c_void
963 ) -> c_int;
964
965/// Mirrors the print callback function pointer of Hexchat.
966type C_PrintCallback = extern "C"
967 fn(word : *const *const c_char,
968 user_data : *mut c_void
969 ) -> c_int;
970
971/// Mirrors the timer callback function pointer of Hexchat.
972type C_TimerCallback = extern "C"
973 fn(user_data : *mut c_void
974 ) -> c_int;
975
976/// Mirrors the print attr callback function pointer of Hexchat.
977type C_AttrCallback = extern "C"
978 fn(word : *const *const c_char,
979 attrs : *const EventAttrs,
980 user_data : *mut c_void
981 ) -> c_int;
982
983/// Mirrors the FD related callback function pointer of Hexchat.
984type C_FDCallback = extern "C"
985 fn(fd : c_int,
986 flags : c_int,
987 udata : *mut c_void
988 ) -> c_int;
989}
990
991/// Mirrors the C struct for `hexchat_event_attrs`. It holds the timestamps
992/// for the callback invocations for callbacks registered using
993/// `hexchat_print_attrs()`, and similar commands.
994#[repr(C)]
995pub struct EventAttrs {
996 pub server_time_utc : time_t
997}
998
999impl fmt::Debug for Hexchat {
1000 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1001 f.debug_struct("Hexchat")
1002 .field("raw_function_pointers", &"...")
1003 .finish()
1004 }
1005}
1006
1007/// This struct mirrors the C Hexchat struct passed to the plugin from
1008/// Hexchat when the plugin is loaded. Hexchat's API is implemented as a struct
1009/// holding callbacks to its native functions. *Don't modify* this struct,
1010/// unless there has been a change to the layout of it in the Hexchat C code
1011/// base.
1012#[repr(C)]
1013pub struct Hexchat {
1014 pub (crate)
1015 c_hook_command : unsafe extern "C"
1016 fn(hp : *const Hexchat,
1017 name : *const c_char,
1018 pri : c_int,
1019 cb : C_Callback,
1020 help : *const c_char,
1021 udata : *mut c_void
1022 ) -> *const hexchat_hook,
1023 pub (crate)
1024 c_hook_server : unsafe extern "C"
1025 fn(hp : *const Hexchat,
1026 name : *const c_char,
1027 pri : c_int,
1028 cb : C_Callback,
1029 udata : *mut c_void
1030 ) -> *const hexchat_hook,
1031 pub (crate)
1032 c_hook_print : unsafe extern "C"
1033 fn(hp : *const Hexchat,
1034 name : *const c_char,
1035 pri : c_int,
1036 cb : C_PrintCallback,
1037 udata : *mut c_void
1038 ) -> *const hexchat_hook,
1039 pub (crate)
1040 c_hook_timer : unsafe extern "C"
1041 fn(hp : *const Hexchat,
1042 timeout: c_int,
1043 cb : C_TimerCallback,
1044 udata : *mut c_void
1045 ) -> *const hexchat_hook,
1046 pub (crate)
1047 c_hook_fd : unsafe extern "C"
1048 fn(hp : *const Hexchat,
1049 fd : c_int,
1050 flags : c_int,
1051 cb : C_FDCallback,
1052 udata : *mut c_void
1053 ) -> *const hexchat_hook,
1054 pub (crate)
1055 c_unhook : unsafe extern "C"
1056 fn(hp : *const Hexchat,
1057 hook : *const hexchat_hook,
1058 ) -> *const c_void,
1059 pub (crate)
1060 c_print : unsafe extern "C"
1061 fn(hp : *const Hexchat,
1062 text : *const c_char),
1063 pub (crate)
1064 c_printf : unsafe extern "C"
1065 fn(hp : *const Hexchat,
1066 text : *const c_char,
1067 ...),
1068 pub (crate)
1069 c_command : unsafe extern "C"
1070 fn(hp : *const Hexchat,
1071 command: *const c_char),
1072 pub (crate)
1073 c_commandf : unsafe extern "C"
1074 fn(hp : *const Hexchat,
1075 command: *const c_char,
1076 ...),
1077 pub (crate)
1078 c_nickcmp : unsafe extern "C"
1079 fn(hp : *const Hexchat,
1080 s1 : *const c_char,
1081 s2 : *const c_char
1082 ) -> c_int,
1083 pub (crate)
1084 c_set_context : unsafe extern "C"
1085 fn(hp : *const Hexchat,
1086 ctx : *const hexchat_context
1087 ) -> c_int,
1088 pub (crate)
1089 c_find_context : unsafe extern "C"
1090 fn(hp : *const Hexchat,
1091 srv_name : *const c_char,
1092 channel : *const c_char,
1093 ) -> *const hexchat_context,
1094 pub (crate)
1095 c_get_context : unsafe extern "C"
1096 fn(hp: *const Hexchat
1097 ) -> *const hexchat_context,
1098 pub (crate)
1099 c_get_info : unsafe extern "C"
1100 fn(hp : *const Hexchat,
1101 id : *const c_char,
1102 ) -> *const c_char,
1103 pub (crate)
1104 c_get_prefs : unsafe extern "C"
1105 fn(hp : *const Hexchat,
1106 name : *const c_char,
1107 string : *mut *const c_char,
1108 integer: *mut c_int
1109 ) -> c_int,
1110 pub (crate)
1111 c_list_get : unsafe extern "C"
1112 fn(hp : *const Hexchat,
1113 name : *const c_char
1114 ) -> *const hexchat_list,
1115 pub (crate)
1116 c_list_free : unsafe extern "C"
1117 fn(hp : *const Hexchat,
1118 hclist : *const hexchat_list
1119 ),
1120 pub (crate)
1121 c_list_fields : unsafe extern "C"
1122 fn(hp : *const Hexchat,
1123 name : *const c_char
1124 ) -> *const *const c_char,
1125 pub (crate)
1126 c_list_next : unsafe extern "C"
1127 fn(hp : *const Hexchat,
1128 hclist : *const hexchat_list
1129 ) -> c_int,
1130 pub (crate)
1131 c_list_str : unsafe extern "C"
1132 fn(hp : *const Hexchat,
1133 hclist : *const hexchat_list,
1134 field : *const c_char
1135 ) -> *const c_char,
1136 pub (crate)
1137 c_list_int : unsafe extern "C"
1138 fn(hp : *const Hexchat,
1139 hclist : *const hexchat_list,
1140 field : *const c_char
1141 ) -> c_int,
1142 pub (crate)
1143 c_plugingui_add : unsafe extern "C"
1144 fn(hp : *const Hexchat,
1145 filename : *const c_char,
1146 name : *const c_char,
1147 desc : *const c_char,
1148 version : *const c_char,
1149 reserved : *const c_char
1150 ) -> *const c_void,
1151 pub (crate)
1152 c_plugingui_remove : unsafe extern "C"
1153 fn(hp: *const Hexchat, handle: *const c_void),
1154 pub (crate)
1155 c_emit_print : unsafe extern "C"
1156 fn(hp : *const Hexchat,
1157 event_name : *const c_char,
1158 ...
1159 ) -> c_int,
1160 pub (crate)
1161 c_read_fd : unsafe extern "C"
1162 fn(hp : *const Hexchat,
1163 src : *const c_void,
1164 buf : *mut c_char,
1165 len : *mut c_int
1166 ) -> c_int,
1167 pub (crate)
1168 c_list_time : unsafe extern "C"
1169 fn(hp : *const Hexchat,
1170 hclist : *const hexchat_list,
1171 name : *const c_char
1172 ) -> time_t,
1173 pub (crate)
1174 c_gettext : unsafe extern "C"
1175 fn(hp : *const Hexchat,
1176 msgid : *const c_char
1177 ) -> *const c_char,
1178 pub (crate)
1179 c_send_modes : unsafe extern "C"
1180 fn(hp : *const Hexchat,
1181 targets : *const *const c_char,
1182 n_targets : c_int,
1183 modes_per_line : c_int,
1184 sign : c_char,
1185 mode : c_char
1186 ),
1187 pub (crate)
1188 c_strip : unsafe extern "C"
1189 fn(hp : *const Hexchat,
1190 string : *const c_char,
1191 len : c_int,
1192 flags : c_int
1193 ) -> *const c_char,
1194 pub (crate)
1195 c_free : unsafe extern "C"
1196 fn(hp : *const Hexchat,
1197 ptr : *const c_void,
1198 ),
1199 pub (crate)
1200 c_pluginpref_set_str : unsafe extern "C"
1201 fn(hp : *const Hexchat,
1202 var : *const c_char,
1203 value : *const c_char
1204 ) -> c_int,
1205 pub (crate)
1206 c_pluginpref_get_str : unsafe extern "C"
1207 fn(hp : *const Hexchat,
1208 var : *const c_char,
1209 dest : *mut c_char
1210 ) -> c_int,
1211 pub (crate)
1212 c_pluginpref_set_int : unsafe extern "C"
1213 fn(hp : *const Hexchat,
1214 var : *const c_char,
1215 value : c_int
1216 ) -> c_int,
1217 pub (crate)
1218 c_pluginpref_get_int : unsafe extern "C"
1219 fn(hp : *const Hexchat,
1220 var : *const c_char
1221 ) -> c_int,
1222 pub (crate)
1223 c_pluginpref_delete : unsafe extern "C"
1224 fn(hp : *const Hexchat,
1225 var : *const c_char
1226 ) -> c_int,
1227 pub (crate)
1228 c_pluginpref_list : unsafe extern "C"
1229 fn(hp : *const Hexchat,
1230 dest : *mut c_char
1231 ) -> c_int,
1232 pub (crate)
1233 c_hook_server_attrs : unsafe extern "C"
1234 fn(hp : *const Hexchat,
1235 name : *const c_char,
1236 pri : c_int,
1237 cb : C_AttrCallback,
1238 udata : *const c_void
1239 ) -> *const hexchat_hook,
1240 pub (crate)
1241 c_hook_print_attrs : unsafe extern "C"
1242 fn(hp : *const Hexchat,
1243 name : *const c_char,
1244 pri : c_int,
1245 cb : C_AttrCallback,
1246 udata : *const c_void
1247 ) -> *const hexchat_hook,
1248 pub (crate)
1249 c_emit_print_attrs : unsafe extern "C"
1250 fn(hp : *const Hexchat,
1251 attrs : *const EventAttrs,
1252 event_name : *const c_char,
1253 ...
1254 ) -> c_int,
1255 pub (crate)
1256 c_event_attrs_create : unsafe extern "C"
1257 fn(hp: *const Hexchat) -> *mut EventAttrs,
1258 pub (crate)
1259 c_event_attrs_free : unsafe extern "C"
1260 fn(hp : *const Hexchat,
1261 attrs : *mut EventAttrs
1262 ),
1263}