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